GHUnit is quite useful for running and reporting tests (unit or otherwise) for iOS projects, and I’ve been using it for a while with good results. Recently, however, I found as I wrote more and more integration-style tests with a remote HTTP service I found the code getting to be a pain to write and maintain. There were two reasons for this:

GHAssert variants fail with an Exception, and therefore other tests do not continue to execute.

This was a real drag, since the assert macros are pretty handy, but I want all my tests to run, even if some fail (gasp I know!).

So, I ended up replacing the GHAsserts with a simple conditional, which doesn’t feel as clean, but navigates around the issue.

Selector names became copy and paste heavy.

Since I’m making notification calls like [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testAsynchronousOperation)]; from within the same method it was additional grunt work to copy the selector name to all the places I needed a reference to the selector.

I thought of adding a SEL mySelector = @selector(foo); to each method, which would cut down on the copy & pasting, but that just didn’t seem clean to me.

I discovered there is an Objective C variable like self called _cmd* which is a reference to the current selector(!). That’s cool, and simplifies code like my testing code a lot.

Sample

For the specific kinds of test cases I was writing, here’s an example which shows both of these issues resolved:

#import <GHUnitIOS/GHUnit.h>
@interface RemoteDataServiceTest : GHAsyncTestCase { }
@end
@implementation RemoteDataServiceTest
- (BOOL)shouldRunOnMainThread
{
// By default NO, but if you have a UI test or test dependent on running on the main thread return YES.
// Also an async test that calls back on the main thread, you'll probably want to return YES.
return NO;
}
- (void)testAsynchronousOperation
{
// Call prepare to setup the asynchronous action.
// This helps in cases where the action is synchronous and the
// action occurs before the wait is actually called.
[self prepare];
[RemoteDataService doNiftyStuffRemotelyWithCompletedBlock:^(id result){
if (!result)
{
GHTestLog(@"result from remote service was unexpectedly nil.");
[self notify:kGHUnitWaitStatusFailure forSelector:_cmd];
return;
}
GHTestLog(@"We got back: %@", result);
[self notify:kGHUnitWaitStatusSuccess forSelector:_cmd];
} errorBlock:^(NSError *error){
GHTestLog(@"Error while doing nifty stuff: %@", [error localizedDescription]);
[self notify:kGHUnitWaitStatusFailure forSelector:_cmd];
}];
// Wait until notify called for timeout (seconds); If notify is not called with kGHUnitWaitStatusSuccess then
// we will throw an error.
[self waitForStatus:kGHUnitWaitStatusSuccess timeout:30.0];
}