aku-aku: v.. To move a tall, flat bottomed object (such as a bookshelf) by swiveling it alternatively on its corners in a "walking" fashion. [After the book by Thor Heyerdahl theorising the statues of Easter Island were moved in this fashion.] source: LangMaker.com. Aku Aku also has another meaning to the islanders: a spiritual guide.
« My Beating Heart | Main Page
Test Driving iPhone Development with XCode 3.1
Posted by dav at 2009 August 20 03:52 PM
File under:

I'm about to start spending a lot of time developing for the iPhone, so I wanted to figure out how to test drive it. Some google searching turned up a lot of information, but most of it was published some time ago and contained outdated details. Most importantly, there is no longer any need to install the OCUnit framework as it now ships with XCode.

I decided to publish a quick run through of what worked for me on XCode 3.1.2. I'll test drive development of a static library here, and then use that tested static library in a new iPhone app. Before you get going on this I suggest you are already capable of building an iPhone app, as I won't spend a lot of time on the details of that. There seems to be a way to write unit test for UI code as well, but I have not got that far yet.

Get Google Toolbox for Mac (GTM)

cd ~/code
svn checkout http://google-toolbox-for-mac.googlecode.com/svn/trunk/ google-toolbox-for-mac

Create the iPhone static library project and set it up for unit testing

Create new iPhone Static Library project via File -> New Project -> iPhone: Library / Cocoa Touch Library

- Save to ~/code/Lumosity

- Add a Unit Test target

- Add a new Run Script build phase to the Unit Test target

- Enter the following as the script (with your path of course), and close the window with the X button at upper left:

- Add the GTM sources to the Unit Test target by right clicking on the Unit Test target, then Add Existing Files. Navigate to your google-toolbox-for-mac checkout directory and add all of the following:

google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestMain.m
google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.m
google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.h
google-toolbox-for-mac/UnitTesting/GTMSenTestCase.m
google-toolbox-for-mac/UnitTesting/GTMSenTestCase.h

and

google-toolbox-for-mac/GTMDefines.h

- Switch your Active Target to Unit Test and then build.

- Bring up your Build Results window with Shift-Command-B or via the XCode Build menu.

You may need to click on the horizonal-lines icon in the middle tool bar to bring up the output, then you should see something like this:

Add a unit test

Now let's add a unit test. Create w new group/folder under the Lumosity project called 'test'. Then right click on that folder and create a new file using the iPhone OS: Cocoa Touch Class / Objective-C test case class. Call it LumosityServer.m. Make sure that you also create a .h file and select only the Unit Test target.

In LumosityServerTest.h change the SenTesting import line, set the USE_DEPENDENT_UNIT_TEST to 0, add your library header and replace a template method declaration with your own. The example test here is fairly meaningless but should serve to explain the process.

#define USE_DEPENDENT_UNIT_TEST 1
#import <SenTestingKit/SenTestingKit.h>
#import <UIKit/UIKit.h>
import "LumosityServer.h" as required
@interface LumosityServerTest : SenTestCase {
}
#if USE_DEPENDENT_UNIT_TEST
- (void) testAppDelegate; // simple test on application
#else
- (void) testMath; // simple standalone test
#endif
@end

becomes:

#define USE_DEPENDENT_UNIT_TEST 0
#import "GTMSenTestCase.h"
#import <UIKit/UIKit.h>
#import "LumosityServer.h"
@interface LumosityServerTest : SenTestCase {
}
#if USE_DEPENDENT_UNIT_TEST
- (void) testAppDelegate; // simple test on application
#else
- (void) testInitWithUser; // simple standalone test
#endif
@end

Now write the test in LumosityServerTest.m. You'll be replacing the template testMath method. Note that STAssertEqualStrings() is from the Google Toolbox for Mac library.

#import "LumosityServerTest.h"
@implementation LumosityServerTest
#if USE_DEPENDENT_UNIT_TEST // all "code under test" is in the iPhone Application
- (void) testAppDelegate {
id yourApplicationDelegate = [[UIApplication sharedApplication] delegate];
STAssertNotNil(yourApplicationDelegate, @"UIAppliation failed to find the AppDelegate");
}
#else // all "code under test" must be linked into the Unit Test bundle
- (void) testInitWithUser {
NSString *username = @"alice";
LumosityServer *lumosity = [[LumosityServer alloc] initWithUser: username];
STAssertEqualStrings(username, [lumosity user], @"user did not match");
[lumosity release];
}
#endif
@end

Run the test (build the Unit Test target, and look in the build log for output). You should now get the expected error of "LumosityServer.h: no such file or directory." The next step is obvious (thanks TDD!), let's create the LumosityServer header and implementation. Right click on the Classes group in the project browser and Add -> New File -> iPhone OS : Cocoa Touch Class / Objective-C class. Name it LumosityServer, check the .h box and add to both Lumosity and Unit Test targets.

Start with this header:

#import <Foundation/Foundation.h>
@interface LumosityServer : NSObject {
NSString* user;
}
@property (nonatomic, retain) NSString *user;
- (id) initWithUser: (NSString*)username;
@end

and this implementation:

#import "LumosityServer.h"
@implementation LumosityServer
@synthesize user;
- (id) initWithUser: (NSString*)username {
self = [super init];
return self;
}

Run the test again and you'll get the expected error of

Almost there! Add a line in the LumosityServer implementation for the passing test:

- (id) initWithUser: (NSString*)username {
self = [super init];
user = username;
return self;
}

Run the unit test again and you should get this:

Test Suite '/Users/dav/code/iphone/Lumosity/build/Debug-iphonesimulator/Unit Test.app' finished at 2009-08-20 11:51:07 -0700.

Executed 1 tests, with 0 failures (0 unexpected) in 0.005 (0.005) seconds

Build that static library file now by switching the Active Target to Lumosity and the Active Build Configuration to Release.

This created the lib file in /Users/dav/code/iphone/Lumosity/build/Release-iphonesimulator/libLumosity.a

Now I can create a new iPhone application called LumosityClient that will use this unit tested library:

Create a new group under Classes for the LumosityLib files

Add the LumosityServer.h and libLumosity.a file into this group:

Now edit the LumosityClientViewController.h:

#import <UIKit/UIKit.h>
@interface LumosityClientViewController : UIViewController {
IBOutlet UILabel *helloLabel; // this is where the username from the LumosityServer instance will be displayed
IBOutlet UITextField *usernameField; // This is where it will be entered by the user
}
- (IBAction) sayHello: (id) sender; // This is the method the button press will invoke
@end

Make sure to save the file and then double click on LumosityClientViewController.xib under the Resources group to bring up Interface Builder. Drag and drop two labels, a text field and a button onto the view

If you right click on the File's Owner you'll see the IBOutlets and IBAction we put in the header. Drag from the little circle to the right of each one over to the corresponding ui widget in the view. usernameField to the TextField and helloLabel to the "Hello?" label. Next right click on the OK button and do a similar thing in the opposite direction, connecting its Touch Up Inside event to the File's Owner icon. When you make this connection you'll see the "sayHello" method name pop up, click on that to complete the connection.

Save and Quit.

Implement the sayHello method in the view controller using the static library class. You'll need to include the LumosityServer.h header.

#import "LumosityClientViewController.h"
#include "LumosityServer.h"
@implementation LumosityClientViewController


- (IBAction) sayHello: (id) sender {
LumosityServer *lumosity = [[LumosityServer alloc] initWithUser:usernameField.text];
NSString *helloMessage = [[NSString alloc] initWithFormat: @"Hello %@" , [lumosity user]];
helloLabel.text = helloMessage;
[helloMessage release];
}
[...]
@end

Build & Go! You are now running your unit tested code in an iPhone application.

Comments:

Post a new comment:

Thanks for signing in, . Now you can comment. (sign out)

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)


Remember me?