Objective C - Unit testing dispatch_async block?

眉间皱痕 提交于 2019-11-30 06:44:42

Run the main loop briefly to let it call the async block:

- (void)testShouldFetchUserDataUsingCorrectId {
   static NSString *userId = @"sdfsdfsdfsdf";
   self.viewController.userId = userId;
   self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]];

   [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId];
   [self.viewController view];
   [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
   [(OCMockObject *)self.viewController.serviceClient verify];
}

I suppose this could fail on a heavily-loaded system, or if you have a bunch of other stuff (timers or other blocks) going on the main thread. If that's the case, you need to run the run loop longer (which slows down your test case), or run it repeatedly until the mock object's expectations have been met or a timeout is reached (which requires adding a method to the mock object to query whether its expectations have been met).

Wrap the execution into a dispatch_group and then wait for the group to finish executing all dispatched blocks via dispatch_group_wait().

Make a dispatch_async wrapper with a similar method signature that in turn calls the real dispatch_async. Dependency inject the wrapper into your production class and use that.

Then make a mock wrapper, which records enqueued blocks and has an extra method for synchronously running all enqueued blocks. Maybe perform a recursive "blockception" if the blocks being executed in turn enqueued more blocks.

In your unit tests, inject the mock wrapper into the system under test. You can then make everything happen synchronously even though the SUT thinks it is doing async work.

dispatch_group sounds like a good solution as well, but would require your production class to "know" to ping the dispatch group at the end of the blocks it enqueues.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!