views:

282

answers:

2

I have an object that fetches XML or JSON over a network. Once this fetching is complete it calls a selector, passing in the returned data. So, for example I'd have something like:

-(void)testResponseWas200
{
    [MyObject get:@"foo.xml" withTarget:self selector:@selector(dataFinishedLoading:)];  
}

I tried the route of implementing dataFinishedLoading in the Test class and attempting to test inside that method, but the test suite is just locking up. This seems like it's a case for mocking, but I'm wondering if others have encountered this and how they handled it.

FYI: I'm using gh-unit for testing and any method prefixed with test* is executed automatically.

A: 

One of the best ways to test asynchronous and multi-threaded code is with event logging. Your code should log events at interesting or useful times. Often an event alone is enough information to prove that logic is working correctly. Somtimes events will need payloads, or other meta information so they can be paired or chained.

This is most useful when the run-time or the operating system supports an efficient and robust eventing mechanism. This enables your product to ship with events in the 'retail' version. In this scenario, your events are only enabled when you need to debug a problem, or run a unit test to prove thins are working correctly.

Having the events in the retail (production) code lets you test and debug on any platform. This is huge benefit over debug or 'checked' code.

Note, like asserts, be careful where you put events - they can be expensive if logged to often. But the good news is that modern OSes and some application frameworks support eventing mechanisms that support 10's of thousands of events easily. Some support taking a stack trace on selected events. This can be very powerful, but usually requires that symbols are available at some point in time - either at logging, or trace post processing time on the target system.

Foredecker
+1  A: 

Asynchronous callbacks often require a message loop to run. It is a frequent pattern to stop the message loop after callback was called in the test code. Otherwise the loop is just waiting for next tasks, and there will be none.

phjr
And you can do this with NSRunLoop, running the loop explicitly in the test method. Only if the asynchronous method actually uses the run loop, of course, but this is true of all of Cocoa's built-in asynchronous methods.
Peter Hosey