In part 2, I explained how to test a bundle by using a custom script in the "Run Script Phase". A slightly better approach is to use a test rig. From the Xcode documentation, a test rig is:

A tool that can take information from its environment and will be passed the path to a unit test bundle as its sole argument.

In part 2, the BundleTestRunner took two arguments, so it cannot be used as a test rig. This entry describes how to modify it so it takes one argument, and the rest of the information from the environment.

Instead of passing the bundle to pre-load as a command line argument, BundleTestRunner will get this information from an environment variable: TEST_LOAD_BUNDLE. Here's the new main method:

int main(int argc, char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSBundle * bundle;
    NSString * bundlePath = [[[NSProcessInfo processInfo] environment]
        objectForKey: @"TEST_LOAD_BUNDLE"];

    if (bundlePath != nil)
    {
        loadBundle(bundlePath);
    }
    
    bundlePath =
        [[[NSProcessInfo processInfo] arguments] objectAtIndex: 1];
    loadBundle(bundlePath);
    SenTestSuite * suite;
    suite = [SenTestSuite testSuiteForBundlePath: bundlePath];
    BOOL hasFailed = ![[suite run] hasSucceeded];
    
    [pool release];
    return ((int) hasFailed);
    return 0;
}

I've not included the the loadBundle() function. It just loads a bundle or throws an exception and can be found in BundleTestRunner.m. Since this version now uses exceptions, Objective-C exception handling must be enabled. Here is a summary of the custom settings needed for BundleTestRunner:

Build SettingValue
Zero Link Off
Other Linker Flags -framework'Foundation
-framework'Appkit
-framework'SenTestingKit
-force_flat_namespace
Enable Objective-C Exceptions On

For the unit test bundle, I still assume you are testing against MyBundle.bundle. Instead of editing the script in the "Run Script Phase", you will leave that as the default. All changes can now be done with build settings. Here is a summary of the changes:

Build SettingValue
Bundle Loader $(BUILT_PRODUCTS_DIR)/MyBundle.bundle/Contents/MacOS/MyBundle
Test Rig ${BUILT_PRODUCTS_DIR}/BundleTestRunner
TEST_LOAD_BUNDLE ${BUILT_PRODUCTS_DIR}/MyBundle.bundle

The last setting, TEST_LOAD_BUNDLE, is a custom build setting, which can be added by clicking the Plus ("+") button. Custom build settings are turned into environment variables, which is how it gets passed to BundleTestRunner.

And that's all! With these build settings, you should now be able to build the unit test bundle and have the tests run as part of the build phase. A possible improvement is to have TEST_LOAD_BUNDLE be a colon separated list of bundles to load, so BundleTestRunner could pre-load multiple bundles. This is probably not a common case, so it's not worth the effort.