Monday, June 6, 2016

XCTest Assertion Tips

1. Pass actual before expected value to get proper failure messages.
2. Most failures are self-explanatory and should not have a message string.
3. Let the macros do the comparisons so they can report the failure values.
4. Watch for object comparisons done accidentally as pointer comparisons.
5. Use XCTAssertNil with Objective-C objects so that failures will report object values.
6. Report long values at the end of the failure message rather than in the middle.
7. Omit the expectations wait handler block.
8. Use XCTest’s NSNotification and KVO expectations where they can simplify tests of asynchronous behavior.


1. Pass actual before expected value to get proper failure messages.
This is the reverse of typical Java and C++ unit test assertions.
XCTAssertEqual(numberOfDocs, 3);  // Good.
Putting the actual value second yields a backwards failure message, citing the expected value as wrong:
XCTAssertEqual(3, numberOfDocs);  // Avoid; gives this error:
// error: -[MyTests testDocList] : ((3) equal to (numberOfDocs)) 
// failed: ("3") is not equal to ("2")

2. Most failures are self-explanatory and should not have a message string.
Provide failure messages only when they offer helpful additional explanation, or to report related values.
XCTAssert(db.hasCompletedLoading); // Good.
XCTAssert(record.hasLoaded, @”record index %d”, index);  // Good.
These assertion messages are redundant with the standard failure messages:
XCTAssert(db.hasCompletedLoading, @”Database did not load”);  // Avoid.
XCTAssertEqual(count, 3, @”count = %d”, count);  // Avoid. 3. Let the macros do the comparisons so they can report the failure values.
XCTAssertGreaterThan(count, 7);  // Good.
XCTAssertEqualObjects(username, @”jsmith”);  // Good.
XCTAssertEqualObjects(resultsArray, @[ @3, @4, @5 ]);  // Good.
XCTAssertEqualWithAccuracy(rightEdge, 3.2, 0.1);  // Good.
These assertion messages will not report the incorrect values:
XCTAssert(count > 7);  // Avoid.
XCTAssert([username isEqual:@”jsmith”]);  // Avoid.
4. Watch for object comparisons done accidentally as pointer comparisons.
XCTAssertEqualObjects(username, @”mjones”);  // Good.
XCTAssertEqual(username, @”mjones”);  // Avoid (usually).
Explicitly document intentional uses of pointer comparison on objects:
// Use pointer comparison to verify that this is the root of the tree.
XCTAssertEqual(currentObject, rootObject);  // Good.
5. Use XCTAssertNil with Objective-C objects so that failures will report object values.
XCTAssertNil(error);  // Good.
XCTAssert(error == nil);  // Avoid.
6. Report long values at the end of the failure message rather than in the middle.
XCTAssert(file.didLoad,
         @”status %d for file: %@”,
         file.status, file.URL.path);  // Good.
Putting smaller values at the end makes them harder to find in the output log:
XCTAssert(file.didLoad,
         @”file: %@ has status %d”,
         file.URL.path, file.status);  // Avoid.
7. Omit the expectations wait handler block.
The wait call requires a handler block, but the block is not really useful.
The wait invocation blocks the test synchronously, and will assert if an expectation is not fulfilled, so providing a block to check the handler’s error parameter is redundant.
[self waitForExpectationsWithTimeout:1.0 handler:nil];  // Good.
[self waitForExpectationsWithTimeout:1.0 handler:^(NSError *error) {
 XCTAssertNil(error);
}];  // Avoid.
8. Use XCTest’s expectationForNotification: and keyValueObservingExpectationForObject: where they can simplify tests of asynchronous behavior.
[self expectationForNotification:kGTMSessionFetcherStoppedNotification
                         object:myFetcher
                        handler:nil];  // Good.