Testing Asynchronous Code With GHUnit

If you’ve ever done a single unit test run on iOS, you know that Apple’s out-of-the-box unit testing sucks. It’s done at build time, instead of runtime, so there’s no way of debugging your test code. Also, if an exception is thrown at any point in your test code, the testing framework crashes with no meaningful error message or pointer as to where the exception occurred.

Well, it turns out that a guy named Gabriel created his own unit testing framework called GHUnit and shared it on github. It’s a great framework that lets you run your tests in runtime, like it was meant to be. Breakpoints, debugging, fun! I won’t get into details here, since you can read about it all in the project’s README file. Great work, Gabriel!

So, I was writing some asynchronous network code the other day, and I wanted to write tests to check my REST resource fetching and parsing. When you issue an asynchronous method call (like NSURLConnection‘s start, for example), a new thread is spawned for that method to run on, and the thread from which you called your asynchronous method continues to run without waiting for the asynchronous method to finish.

This is bad because your tests will obviously fail if you rely on the results of the asynchronous call. There has to be a way to tell your test’s thread to wait for the asynchronous method to finish, and then continue executing.

When you run your GHUnit target and tap the “Run” button to start the tests, you have the following threads running:

1. Main thread: this is the application’s main thread where typically all your code is executed, but since this is the GHUnit test target, your code won’t be executing here. GHUnit runs its own code on this thread. (Note: You can make your code run on the main thread if you implement the -(BOOL)shouldRunOnMainThread method and return YES, but then your GUI will be unresponsive while your tests are being run. We won’t be doing that here.)
2. Tests thread: all your test code is executed on this thread. You start your asynchronous method from here.
3. Asynchronous method thread: this is where your asynchronous code will run, whatever it is – a network operation, your custom NSOperation subclass, etc.

(There are actually more threads, as can be seen in the debugger, but these three are the ones we’re interested in.)

We need to create a typical signal/wait scenario, which we can achieve by utilizing the NSCondition class. Apple documentation mentions a boolean predicate that should be checked before running any code protected by the condition object (which is in our case a call to signal or wait):

lock the condition
while (!(boolean_predicate)) {
    wait on condition
}
do protected work
(optionally, signal or broadcast the condition again or change a predicate value)
unlock the condition

Since we only want to signal our test thread and let it know that the asynchronous thread has finished executing, there’s no need to implement this additional predicate logic. It is necessary in cases where you have multiple threads executing the protected code, and a race condition might occur. In our case, we only have one thread waiting for a condition (your test thread) and one thread signaling the condition (your asynchronous method thread), so a predicate is not needed. Note that this is a special case!

Thus, our logic would be more like:

lock the condition
signal the condition OR wait on condition
unlock the condition

Simple!

On to the code. Here’s an example GHUnit test header file:

We need two variables in this particular case – a condition object which will be used to block the testing thread, and a boolean which will indicate if our asynchronous method finished successfully.

And here’s the implementation file:

In testAsynchronousMethodCall, we create a MyOperation object (which is a fictive NSOperation subclass that you have to implement) and start running its main method on a separate thread. That would be thread number 3 in the list above.

We assume that MyOperation‘s main method calls one of the delegate methods – myOperationDidFinish or myOperationDidFailWithError: – to indicate success or failure. So we set our boolean value connectionSucceeded to reflect that, and then call signal on the condition object, which signals the blocked code on our test thread (number 2 in the list above) to continue executing.

We then test the boolean with STAssertTrue to check if everything went OK, and we’re done.

That’s it! If you have questions or comments, feel free to holler at me. :)

4 responses to “Testing Asynchronous Code With GHUnit”

  1. tom

    thank you for sharing. i use this in my network related test cases. great blog!

  2. Premo

    Why not subclass GHAsyncTestCase and use wait: methods?

  3. Audun

    Thank you for sharing!

  4. BowmanClaire35

    Lots of specialists tell that home loans aid a lot of people to live their own way, just because they can feel free to buy needed stuff. Moreover, some banks give college loan for all people.

Leave a Reply