Asynchronous APIs turn out to be a very useful way of programming, especially in event-based frameworks like Apple’s iOS and OS X. The prevalent idiom for implementing asynchronous APIs is using blocks. Blocks are an implementation, by Apple, to their C, C++ and Objective-C compilers that implement closures. Having functions being first-class citizens via blocks gives the three aforementioned languages a whole new flexibility, while retaining the language’s previous advantages.
Let’s imagine we have a function that takes both a success block and a failure block. I like to name functions of this form somethingOnSuccess:onFailure:. The success block may be called with some results as parameters, and the failure block is called with an NSError instance.
How can we unit test this function? Naively, we can go ahead and test the different scenarios one by one like so.
This sort of thing works quite well for a few tests but as more tests are added, especially when you want to test the failure block, it quickly becomes unwieldy and the DRY principle is violated repeatedly and flagrantly. For tests that consider the success block the failure block boilerplate code is duplicated, and for tests that consider the failure block it’s the success block. Yuck.
Here’s my solution.
Firstly we write a wrapper for calling the doStuffOnSuccess:onFailure: method that bookends it with the call to expectationWithDescription: and waitForExpectationWithTimeout call. This wrapper’s single responsibility is to manage the XCTestExpectation instance and allows tests to be written with slightly fewer lines of code.
Secondly we write two helper functions that call the wrapper. These helpers address the case when we test the success block, and the other the failure block. The boilerplate code for the unused block is not duplicated.
Now our test cases look like this:
I’m pretty happy with this as a pattern for testing completion blocks and as an added bonus the method names and signatures have a pleasing symmetry.