Sunday, February 20, 2011

Test me to the end of code

Those who know me may think I'm Javaholic - well, maybe a little ;) - but I do like other programming languages too :) - really :) - below you may find results of my small experiment with XCode 4, Objective-C categories and Unit Tests.

I've started with the idea of extending NSArray class with method returning an array with shuffled elements, which can be achieved using Objective-C categories [1], in following way:

Define the Shuffling category (NSArray+Shuffling.h):
#import <Foundation/Foundation.h>

@interface NSArray (Shuffling)

- (NSArray *) shuffle;

@end
Implement the category using modern version of Fisher-Yates shuffle algorithm [2] (NSArray+Shuffling.m):
#import "NSArray+Shuffling.h"

@implementation NSArray (Shuffling)

- (NSArray *) shuffle 
{
    NSMutableArray *result = [self mutableCopy];
    int j = 0;
    for (int i = [result count]; i >= 1; i--) {
        j = arc4random() % i;
        [result exchangeObjectAtIndex:j withObjectAtIndex:i-1];
    } 
    return [[result copy] autorelease];
}

@end

We have the code, let's put it into some project where we could test it - static library will be enough for our needs, let's use it:


XCode 4 let you select if you will use Unit Tests for the project when you define its name:


After creating new project, we will add our source code to it:


It will be new Objective-C Category:


based on NSArray:


and bound to the primary project target:


Now we can put the source code described at the beginning of this post into newly created files.

Project created the above way with XCode 4 has automatically generated Unit Test, which can be run at this point to check if everything works correctly - before you do it, check if you selected the iPhone Simulator for running it - it cannot be done on physical device.

Let's run the XCode 4 generated Unit Test:


What we will see at this point will be:


It looks like everything is fine till now :) - let's create our own test instead of STFail macro:
- (void)testExample 
{
    NSArray *array = [NSArray arrayWithObjects: [NSNumber numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3], [NSNumber numberWithInt:4], [NSNumber numberWithInt:5], [NSNumber numberWithInt:6], [NSNumber numberWithInt:7], [NSNumber numberWithInt:8], nil];
    NSArray *shuffledArray = [array shuffle];
    STAssertNotNil(shuffledArray, @"Shuffled array is nil");
    STAssertTrue([shuffledArray retainCount] == 1, @"Retain count: %i", [shuffledArray retainCount]);
    NSLog(@"Shuffled: %@", shuffledArray);
}

When you run it this time you'll get the exception:


because our static library was not used by the linker, and thus our NSArray extension is not visible, let's correct it by adding appropriate linker flag [3] (-all_load):


When you run the test now you should receive something like this:


As you see this time Unit Test is working correctly, and our shuffling code too :)

Few links for the dessert:
  1. Objective-C: Categories
  2. Fisher–Yates shuffle
  3. Objective-C Category Causing unrecognized selector
  4. Test Driving Your Code with OCUnit
  5. OCUnit: Integrated Unit Testing In Xcode