4

The part of a method that I am trying to test is as follows:

- (void)configureTableFooterView {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.tableView.tableFooterView = nil;

        if ([self.parser.resultSet isLastPage]) {
            return;
        }
    });
}

I have written the unit test as follows:

- (void)testTableFooterViewConfigurationAfterLastPageLoaded {

    id mockTableView = OCMClassMock([GMGFlatTableView class]);

    OCMExpect([mockTableView setTableFooterView:[OCMArg isNil]]);

    id resultSet = OCMClassMock([GMGResultSetInfo class]);

    OCMStub([resultSet isLastPage]).andReturn(YES);

    OCMStub([self.mockParser resultSet]).andReturn(resultSet);

    id partialMockSUT = OCMPartialMock(self.sut);

    OCMStub([partialMockSUT tableView]).andReturn(mockTableView);

    [self.sut configureTableFooterView];

    OCMVerifyAllWithDelay(mockTableView, 2.0);

    //OCMVerifyAllWithDelay(partialMockSUT, 2.0); 
}

I have another test in the same class which is testing the same things from with in the dispatch_async call on the main thread. The test expectations and verification setup in that test match this one. While that test passes, this one gets stuck in an infinite loop at the delayed verification step.

Interestingly, if I only run this 1 test, it passes with out any problems. Its only when this test is run with other tests that I see the problem.

UPDATE:

In unit test, execute the block passed in queue with dispatch_asyc

This is a much more relevant post. However, this fails almost in the exact same way as the original test method:

- (void)testTableFooterViewConfigurationAfterLastPageLoaded {

    id mockTableView = OCMClassMock([GMGFlatTableView class]);

    OCMExpect([mockTableView setTableFooterView:[OCMArg isNil]]);

    id resultSet = OCMClassMock([GMGResultSetInfo class]);

    OCMStub([resultSet isLastPage]).andReturn(YES);

    OCMStub([self.mockParser resultSet]).andReturn(resultSet);

    id partialMockSUT = OCMPartialMock(self.sut);

    OCMStub([partialMockSUT tableView]).andReturn(mockTableView);

    [self.sut configureTableFooterView];

    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];

    OCMVerifyAll(mockTableView);
}

The line with NSRunLoop crashes with EXC_BAD_ACCESS when run as suite but runs fine alone!

Community
  • 1
  • 1
Numan Tariq
  • 1,296
  • 1
  • 9
  • 18
  • Have you checked http://stackoverflow.com/questions/18667355/how-to-test-async-method-in-block-using-ocmock ? – jcaron Sep 12 '16 at 16:32
  • Note that this is more suitable for UI Testing, not Unit Testing – Cristik Sep 12 '16 at 21:25
  • @Cristik I understand but the method under test is fairly standalone and I want to use unit tests to determine it behaves correctly on its own. The UI Tests will make sure that the method is called in the UI flow correctly. However, the problem can be generalized to GCD in general so my question stands – Numan Tariq Sep 13 '16 at 08:48
  • @jcaron That question deals with blocks when used as callbacks. This particular case is for GCD. The test should work and it does on its own. I want to know why it fails when run in the suite – Numan Tariq Sep 13 '16 at 08:48

1 Answers1

1

You can make class wrapper around dispatch_async, and pass it as dependency. Also you can make fake wrapper, and pass it in tests. If you interested in, I can provide much more detailed explanation.

Alfred Zien
  • 1,025
  • 1
  • 11
  • 31
  • Do you mean write a wrapper class around the dispatch_async C functions? If so, I don't really like that idea. Also, my current test configuration is actually working as long as I perform that test on its own. Its only when I run all the tests together that it fails. I am missing some thing in my understanding of how it is working – Numan Tariq Sep 15 '16 at 14:56
  • Then your code is not a unit test. If you testing your code with dependencies it is called functional test, and, I don't really figured out why exactly your test failing with others, but in general, it is because of functional tests. – Alfred Zien Sep 15 '16 at 15:01
  • It is also violates single responsibility principle, as your class, that is managing table view, knows about dispatch queues and asynchronous code execution. – Alfred Zien Sep 15 '16 at 15:04