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");
}
I wrote a little helper that makes it easy to test asynchronous API. First the helper:
You can use it like this:
It will only continue if
done
becomesTRUE
, so make sure to set it once completed. Of course you could add a timeout to the helper if you like,St3fan, you are a genius. Thanks a lot!
This is how I did it using your suggestion.
'Downloader' defines a protocol with a method DownloadDidComplete that fires on completion. There's a BOOL member variable 'downloadComplete' that is used to terminate the run loop.
The run-loop could potentially run forever of course.. I'll improve that later!
This is tricky. I think you will need to setup a runloop in your test and also the ability to specify that runloop to your async code. Otherwise the callbacks won't happen since they are executed on a runloop.
I guess you could just run the runloop for s short duration in a loop. And let the callback set some shared status variable. Or maybe even simply ask the callback to terminate the runloop. That way you you know the test is over. You should be able to check for timeouts by stoppng the loop after a certain time. If that happens then a timeout ocurred.
I've never done this but I will have to soon I think. Please do share your results :-)
To elaborate on @St3fan's solution, you can try this after initiating the request:
Another way:
I found this article on this which is a muc http://dadabeatnik.wordpress.com/2013/09/12/xcode-and-asynchronous-unit-testing/
I just wrote a blog entry about this (in fact I started a blog because I thought this was an interesting topic). I ended up using method swizzling so I can call the completion handler using any arguments I want without waiting, which seemed good for unit testing. Something like this:
blog entry for anyone that cares.