Search code examples
iosxcodexcode6ocmockito

Testing blocks with Xcode 6 and OCMockito


I am starting in Unit testing with objective-c and I need to know how to test blocks with OCMockito and Xcode 6.

I am testing an Interactor, this interactor should return an array as a block argument and I has to ask the Provider file for the elements.

This is the method I want to test:

    - (void)userPoiListsWithSuccessBlock:(MNBSavePoisInteractorSuccess)success {
        self.poiListEntityArray = [self.poiListProvider poiListsForUser:self.loggedUser];
        self.poiListViewObjectArray = [self viewPoiListObjectListWithPoiLists:self.poiListEntityArray];
        success(self.poiListViewObjectArray);
    }

First, I setup the elements that I am going to use

    self.mockPoiListProvider = mock([PoiListProvider class]);
    self.sut = [[MNBSavePoisInteractor alloc] initWithManagedObjectContext:self.coreDataStack.managedObjectContext andPoiListProvider:self.mockPoiListProvider];

- (UserEntity *)loggedUserMock {
    UserEntity *mockLoggedUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([UserEntity class]) inManagedObjectContext:self.coreDataStack.managedObjectContext];
    mockLoggedUser.userId=@"1";
    mockLoggedUser.username=@"user";
    mockLoggedUser.loggedUser=@YES;
    return mockLoggedUser;
}

- (InMemoryCoreDataStack *)coreDataStack{
    if (!_coreDataStack) {
        _coreDataStack = [[InMemoryCoreDataStack alloc] init];
    }
    return _coreDataStack;
}

- (PoiListEntity *)poiListFake {
    PoiListEntity *fake = [NSEntityDescription insertNewObjectForEntityForName:@"PoiListEntity" inManagedObjectContext:self.coreDataStack.managedObjectContext];
    fake.name = @"Test";
    fake.poisCount = @2;
   [fake addContributorsObject:[self loggedUserMock]];

    return fake;
}

Then, I do the test. I am using Xcode 6 waitForExpectation to manage the asynchronous methods. I think I am doing something wrong.

- (void)waitForExpectation {
    [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
        if (error) {
            NSLog(@"Timeout Error: %@", error);
        }
    }];
}
- (void)testShouldReturnPoiLists {
    XCTestExpectation *expectation =  [self expectationWithDescription:@"Waiting method ends"];

    [given([self.mockPoiListProvider poiListsForUser:[self loggedUserMock]]) willReturn:@[[self poiListFake]]];

    [self.sut userPoiListsWithSuccessBlock:^(NSArray *results) {
        [expectation fulfill];
           XCTAssert(resutls.count == 1, @"Results %zd", resutls.count);
    }];
    [self waitForExpectation];
}

I understood if I give the object in willReturn in the given method, when I call the sut method that I want to test it should return what I give before. Is that true? Thank you


Solution

  • I see no asynchronous code. You just want a block that captures the results, so use a __block variable to make the results available outside of the block. Then you can assert whatever you want:

    - (void)testShouldReturnPoiLists {
        [given([self.mockPoiListProvider poiListsForUser:[self loggedUserMock]]) willReturn:@[[self poiListFake]]];
        __block NSArray *capturedResults;
    
        [self.sut userPoiListsWithSuccessBlock:^(NSArray *results) {
            capturedResults = results;
        }];
    
        assertThat(capturedResults, hasLengthOf(1));
    }
    

    The relationship between the length of 1 and the fake is hard to tell. Let's also parameterize the faking code:

    - (PoiListEntity *)poiListFakeWithName:(NSString *)name count:(NSNumber *)count {
        PoiListEntity *fake = [NSEntityDescription insertNewObjectForEntityForName:@"PoiListEntity" inManagedObjectContext:self.coreDataStack.managedObjectContext];
        fake.name = name;
        fake.poisCount = count;
        [fake addContributorsObject:[self loggedUserMock]];
        return fake;
    }
    

    With that, we can write more interesting tests.

    I do want to add that it's important to "listen to the tests." There's a lot of convoluted set-up to dance around Core Data. That tells me that if you can rewrite things to be independent of Core Data — completely ignorant of it — everything will be much simpler.