iOS Testing - Testing asynchronous code (Part 4)

This is the part 4 in the series of articles on 'Unit Testing on iOS'. Below is the list of all articles and respective links to them.

  1. iOS Unit Tests - Testing models creation (Part 1)
  2. iOS Unit Testing - Testing View Controller (Part 2)
  3. iOS Unit Testing - Switching method implementations with OCMock (Part 3)
  4. iOS Testing - Testing asynchronous code (Part 4)
  5. iOS Testing - Input fields validation testing (Part 5)

We all deal with asynchronous code multiple time a day. This is useful to keep user engaging and avoid messing with UI while heavy operation happens in the background on background thread. As soon as background operation completes, we switch to main thread and update an UI.

I have following code performing hypothetical asynchronous operation. A method asyncOperationWithBlock is designed to mimic the behavior of async operation by adding delay. We also use the flag operationComplete which will be indicator for completion status of an operation.

Header file


typedef void (^Block)();
@interface JKUnitTestsDemoOperations : NSObject

@property (nonatomic, assign) BOOL operationComplete;
- (void)asyncOperationWithBlock:(Block)block;

@end

Implementation file


@implementation JKUnitTestsDemoOperations
- (void)asyncOperationWithBlock:(Block)block {
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        self.operationComplete = YES;
        block();
    });
}
@end

Unit test for async operation

We will use special construct provided by Specta framework. As taken from Specta Documentation,


it(@"should do some stuff asynchronously", ^{
    waitUntil(^(DoneCallback done) {
      // Async example blocks need to invoke done() callback.
      done();
    });
  });

We will use this API to test our asynchronous block as follows,


it(@"Asynchronous operation Test", ^{
        JKUnitTestsDemoOperations* operation = [[JKUnitTestsDemoOperations alloc] init];
// Operation completion flag should be set to NO before commencement.
        XCTAssert(operation.operationComplete == NO);
// Set up the fix timeout for asynchronous operation. If operation fails to complete by the timeout, test will fail.
        waitUntilTimeout(5.0, ^(DoneCallback done) {
            [operation asyncOperationWithBlock:^{
// Operation completion flag should be set to YES after completion.
                XCTAssert(operation.operationComplete == YES);
// Always need to execute done callback after async block executes.
                done();
            }];
        });
    });

In the next blog post I will show you how to mock and perform validation on input fields (As in the form) from the view controller

Go to next article