Like Unit Test UICollectionView in UIViewController

I want a unit test UICollectionView that is inside a UIViewController (for example, I want to check that the UICollectionView has the number of cells that I expect from it in my unit test)

My unit test is based on the following blog (about how the unit test is a view controller): http://yetanotherdevelopersblog.blogspot.co.il/2012/03/how-to-test-storyboard-ios-view.html

In unit test, I can get a pointer to a UICollectionView in the UIViewController that I am testing. Here is the code in my setUp method:

storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; viewController = [self.storyboard instantiateViewControllerWithIdentifier:@"MainViewController"]; [viewController performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES]; self.collectionView = viewController.collectionView 

Unfortunately, there are no cells in this UICollectionView (i.e. self.collectionView.visibleCells.count is 0, so I cannot access the cells and verify that the data is what I expect), although when I debug it I see that my application add these cells to the collection view.

What am I doing wrong? Any tips and tricks on using unit test UICollectionView in a UIViewController?

Edit: After Roshan's help, I can now narrow down my problem. I am testing a UIViewController module with a UICollectionView. In my unit test, I want to verify that the cell values ​​inside the CollectionView are what I expect. But collectionView:cellForItemAtIndexPath: not called on my ViewController, and therefore my cells do not exist when it is called as unit test. Any idea?

+6
source share
2 answers

When implementing TTD on iOS, try not to rely on a system that invokes delegate and data source methods.

You should call these methods directly from your unit tests. It's just a matter of setting up the right environment for your tests.

For example, when I implement TDD for UICollectionView, I create two separate classes specifically for implementing the UICollectionViewDataSource and UICollectionViewDelegate protocols, which create a separation of problems, and I can unit test these classes separately access the view controller itself, although I still need to initialize the view controller to configure hierarchies of representations.

Here's an example, headers and other minor code snippets are missing, of course.

UICollectionViewDataSource example

 @implementation CellContentDataSource @synthesize someModelObjectReference = __someModelObjectReference; #pragma mark - UICollectionViewDataSource Protocol implementation - (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return __someModelObjectReference ? [[__someModelObjectReference modelObjects] count] : 0; } - (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { static NSString * const kCellReuseIdentifer = @"Cell"; UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifer forIndexPath:indexPath]; ModelObject *modelObject = [__someModelObjectReference modelObjects][[indexPath item]]; /* Various setter methods on your cell with the model object */ return cell; } @end 

Unit Test Example

 - (void) testUICollectionViewDataSource { UIStoryBoard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; MainViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"MainViewController"]; [viewController view]; // Loads the view hierarchy // Using OCMock to mock the returning of the model objects from the model object reference. // The helper method referenced builds an array of test objects for the collection view to reference id modelObjectMock = [OCMockObject mockForClass:[SomeModelObjectReference class]]; [[[modelObjectMock stub] andReturn:[self buildModelObjectsForTest]] modelObjects]; CellContentDataSource *dataSource = [CellContentDataSource new]; dataSource.someModelObjectReference = modelObjectMock; viewController.collectionView.dataSource = dataSource; // Here we call the data source method directly UICollectionViewCell *cell = [dataSource collectionView:viewController.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; XCTAssertNotNil(cell, @"Cell should not be nil"); // Assert your cells contents here based on your test model objects } 
+4
source

I don’t know exactly where the problem is, but the Apple documentation says loadView about it:

You should never call this method directly. The view controller calls this method when it requests a view property, but it is currently zero. If you create your views manually, you must override this method and use it to create your views. If you use Interface Builder to create your views and initialize the view controller, that is, you initialize the view using the initWithNibName: bundle method: directly set the nibName and nibBundle properties or create both views and the view controller in the Builder interface, then you should not override this method .

So, instead of calling directly, we call [viewController view] to make the view load.

As for your problem, check if viewController.collectionView 0 or not. It is possible that your outlet is not yet installed.

+2
source

Source: https://habr.com/ru/post/956819/


All Articles