0

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

croigsalvador
  • 1,993
  • 2
  • 24
  • 47
  • I don't understand the intent of your test — party because I don't understand the intent of the method under test. Can you explain what `-userPoiListsWithSuccessBlock:` is, and what you want to test? – Jon Reid Feb 07 '15 at 20:54
  • Sorry @JonReid , actually that wasn't the real method. I have updated it. This method returns an array of ``'s. The array has two kinds of objects that conforms the protocol . The `PoiListProvider` method returns Entity items. The second method returns `` that are `Ponsos` that I convert from the entities and three custom objects. I want to test if the results array has the same number of objects that I need. I thought if I set `willReturn` with that entity I will catch that in the block. I am sorry I don't understand well what I should do with this tests – croigsalvador Feb 09 '15 at 19:48

1 Answers1

1

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.

Jon Reid
  • 20,545
  • 2
  • 64
  • 95
  • Sorry @JonReid, I was very busy and I couldn't try it, but it worked. I have one new question, what should I do If `poiListProvider` call a method with a `block` that gives me a `NSArray` and that is the `results NSArray` that I pass as a parameter in the `successBlock `? What should I test in the interactor? Should I verify he invocation of the poiList method when I call the `interactor userPoiListWith...`? Should I test the returned parameter in the block? I am so sorry, but I don't get this very well. If you prefer I can post a new question. Thank you – croigsalvador Mar 05 '15 at 23:28
  • Yes, a new question would probably be helpful. – Jon Reid Mar 06 '15 at 03:33