Wednesday, September 9, 2009

 

Weird, man, weird

So I have switched to the Google Toolbox iPhone unit-test framework, and it mostly works a dream. You plug it in as per those instructions, write some tests, switch to your test target, hit "Build", and it runs your unit tests in the build phase, and highlights test failures in the same way that XCode normally highlights compile errors.

However. I have found something profoundly weird about it, which cost me a bunch of today.

Suppose your unit test is failing, and you want to debug it. Well, you can; but to do so, you have to not just Build but Run the test target, so you have to comment out the tests that are causing the build to fail. Easy enough. Then the test target launches normally in the simulator, and you can step through with the debugger, and find out what went wrong.

However. Guess what? If you leave any breakpoints in your code, and then go back and hit Build, then the Google unit-test framework will begin to fail, in strange and inexplicable ways (mostly null values where there shouldn't be any, in my case.) Remove those breakpoints - and poof, your unit tests are working again.

May you not spend a day beating your head against this, as I did.

Labels: , , , , , , , , ,


Monday, August 3, 2009

 

if you love some source code, set it free

So I have gone and

To be honest I would have liked another few days for cleanup and further testing, but I've gone and gotten a real job (iPhone app development, again) that will occupy me for at least a couple of months, so I had to hurry things up. I apologize for the ugliness of the code.

Some of that ugliness is due to its extremely crude testing framework. Unit testing, the panacea of modern software development, is bizarrely difficult to integrate with the iPhone SDK. There are various tools to help you - one created by Google - but they're all far more complex and kludgy than I'd like, especially compared to the elegant joy that is GAEUnit.

It turns out, however, that it is possible to seamlessly integrate unit tests and test-driven development into iPhone development with XCode. There are a few caveats, but overall, these instructions work very well, if you follow them very carefully.

The caveat is that you can only run the unit tests on an actual physical device, not in the simulator. Now, in some ways this is actually a bonus - you avoid the "but it works in the simulator!" pitfall, and I have learned that the only way to get a feel for what your UI should be is to work on a real iPhone or iPod Touch, the simulator is disastrously misleading - but it does mean that you'll have to go ahead and shell out the $100 to join the iPhone Developer Program so that you can generate the certificates needed to sign your app and install it on your device.

Which in turn is a bit of a hassle. But it's a necessary hassle, and one you should get out of the way as soon as possible. "Build early, build often," is a motto that has served me well for many years.

The device-testing is also limited in that you have to specifically select a test target, so you can't automatically run your unit tests every time you build your app. But as long as you remember to unit-test at least daily, and immediately after making major changes, I can't see this as that big a shortcoming.

Here's some code to run tests in their own separate database, so that you don't pollute your application's data.

Your test case's .h file looks something like this:

// Dependent unit tests mean unit test code depends on an application to be injected into.
// Setting this to 0 means the unit test code is designed to be linked into an independent executable.
#define USE_DEPENDENT_UNIT_TEST 1

#import <SenTestingKit/SenTestingKit.h>
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"


@interface MyTestCase : SenTestCase {
NSManagedObjectContext *context;
}

@end


and its .m:


-(void) setUp {
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSURL *storeUrl = [NSURL fileURLWithPath: [[appDelegate applicationDocumentsDirectory] stringByAppendingPathComponent: @"MyAppTest.sqlite"]];

NSError *error;
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [appDelegate managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
// Handle error
NSLog(@"Error creating persistent store MyAppTest.sqlite %@, %@", error, [error userInfo]);
}
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator: persistentStoreCoordinator];
}

-(void) tearDown {
[context release];
}

Labels: , , , , ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]