How to unit test asynchronous APIs?

2019-01-08 04:04发布

I have installed Google Toolbox for Mac into Xcode and followed the instructions to set up unit testing found here.

It all works great, and I can test my synchronous methods on all my objects absolutely fine. However, most of the complex APIs I actually want to test return results asynchronously via calling a method on a delegate - for example a call to a file download and update system will return immediately and then run a -fileDownloadDidComplete: method when the file finishes downloading.

How would I test this as a unit test?

It seems like I'd want to the testDownload function, or at least the test framework to 'wait' for fileDownloadDidComplete: method to run.

EDIT: I've now switched to using the XCode built-in XCTest system and have found that TVRSMonitor on Github provides a dead easy way to use semaphores to wait for async operations to complete.

For example:

- (void)testLogin {
  TRVSMonitor *monitor = [TRVSMonitor monitor];
  __block NSString *theToken;

  [[Server instance] loginWithUsername:@"foo" password:@"bar"
                               success:^(NSString *token) {
                                   theToken = token;
                                   [monitor signal];
                               }

                               failure:^(NSError *error) {
                                   [monitor signal];
                               }];

  [monitor wait];

  XCTAssert(theToken, @"Getting token");
}

13条回答
相关推荐>>
2楼-- · 2019-01-08 04:32

My answer is that unit testing, conceptually, is not suitable for testing asynch operations. An asynch operation, such as a request to the server and the handling of the response, happens not in one unit but in two units.

To relate the response to the request you must either somehow block execution between the two units, or maintain global data. If you block execution then your program is not executing normally, and if you maintain global data you have added extraneous functionality that may itself contain errors. Either solution violates the whole idea of unit testing and requires you to insert special testing code into your application; and then after your unit testing, you will still have to turn off your testing code and do old-fashioned "manual" testing. The time and effort spent on unit testing is then at least partly wasted.

查看更多
登录 后发表回答