The Run Loop In Cocoa Unit Tests
February 26, 2012

Following on from my post on parameterised unit tests, another little utility hidden away in ECUnitTests is some support for running the run loop in unit tests.

Why would you want to do that, I hear you ask?

The answer is: because you want to do something asynchronous in a unit test, which relies on the run loop. An example would be using NSURLConnection to download something.

The Problem

Each unit test typically runs as a single, discreet test method.

If you need to use something like NSURLConnection that relies on the run loop to post notifications or call delegate methods, then you have a problem doing so in a unit test method. The asynchronous stuff won’t run until after your test method has exited (if at all), at which point it’s too late to test the results.

The Solution

The solution is simply to set things up, call your asynchronous method, then run the run loop until something flags that it’s time to stop, and finally test your results.

This is actually very simple. All you need is to set up some sort of boolean variable which you can test to see if it’s time to exit the loop.

You then set it to false, and enter a loop like this:

exitRunLoop = NO;
while (!exitRunLoop)
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

In order that you don’t get stuck in this loop forever, you need a way for one of your delegate methods or completion blocks to set the value of exitRunLoop to YES.

The easiest way to do this is to make exitRunLoop a property on the test class, and make sure that the test class is also the delegate (or accessible from the delegate or completion block).

To simplify things, the ECTestCase handles this for you, and provides two methods that you can call.

- (void)runUntilTimeToExit;
- (void)timeToExitRunLoop;

If you inherit from ECTestCase in your test class, you can simply call the first one of these when your test needs to wait for something to happen, and call the second one when the thing has happened and you want to stop waiting.

You can see an example of this in ECUnitTests.