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"); }
Async unit tests that return Task have none of the problems of async unit tests that return void. Async unit tests that return Task enjoy wide support from almost all unit test frameworks.
To test asynchronous code, we use the XCTestExpectation class and wait for the expected outcome. The workflow is to create an expectation, and then when the asynchronous task completes successfully, we fulfil that expectation. We will wait for a specific amount of time for the expectation to be fulfilled.
There is an alternate form of test that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called done . Jest will wait until the done callback is called before finishing the test.
I ran into the same question and found a different solution that works for me.
I use the "old school" approach for turning async operations into a sync flow by using a semaphore as follows:
// create the object that will perform an async operation MyConnection *conn = [MyConnection new]; STAssertNotNil (conn, @"MyConnection init failed"); // create the semaphore and lock it once before we start // the async operation NSConditionLock *tl = [NSConditionLock new]; self.theLock = tl; [tl release]; // start the async operation self.testState = 0; [conn doItAsyncWithDelegate:self]; // now lock the semaphore - which will block this thread until // [self.theLock unlockWithCondition:1] gets invoked [self.theLock lockWhenCondition:1]; // make sure the async callback did in fact happen by // checking whether it modified a variable STAssertTrue (self.testState != 0, @"delegate did not get called"); // we're done [self.theLock release]; self.theLock = nil; [conn release];
Make sure to invoke
[self.theLock unlockWithCondition:1];
In the delegate(s) then.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With