Testing with XCTest (objc.io-15)

In this post, what I have learned from Objc.io and NSHispter/xctestcase/ about the famous Apple’s XCTest.

Tests provide us:

  • A level of comfort with code quality
  • Refactor knowing we didn’t break things


  • Tests are grouped in classes that are subclass from XCTestCase
  • method starts with ‘test’ is a test
  • we can add @property & helper methods to these classes
  • we can put all our helper methods into a common super class (sub of XCTestCase)

Method naming:

  • “testThatIt” prefix -> the focus is in desired outcome
  • add DISABLED_ to method name to disable test

Method structure:  Given – When – Then

    • Given: creating model objects 
    • When: the code we want to test, usually 1 method call
    • Then: XCTAssert… // check result
  • Repeated code should be grouped in setUp and tearDown methods
  • Implement delegate protocols directly in our XCTestCase

Mocking with OCMock:

  • Mock is an object that returns a prepared answer for method calls
  • Mocks should be used for all dependencies (all other things not in our class) -> we can test behaviour of a class in isolation. 
  • For classes integration, we have integration tests !
  • More to study OCMock
  • in Swift, We can just create a simple inner class, using override to give a mock result for the methods.

Performance Testing:

measureBlock() { 
// the output shows average execution time for this block.

Testing Async methods: 

XC uses XCTestExpectation & waitForExpectationsWithTimeout to test Async methods!

XCTestExpectation has 1 method: fulfill() – you call it means that the Test has been a success. 

The waitForExpectation function specifies how long you will wait for the result. The Handler is when expectation has not been fulfilled within the time frame ! 

- (void)testThatItAppendsAString; 
    NSString *s1 = @"Foo"; 
    XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; 
    [s1 appendString:@"Bar" resultHandler:^(NSString *result){
        XCTAssertEqualObjects(result, @"FooBar"); 
        [expectation fulfill]; 
    // Should always fulfil at the end of callback ! To avoid race condition where run loop may exit before test completed: the operation is cancelled before it finishes. 
    [self waitForExpectationsWithTimeout:0.1 handler:nil];