views:

641

answers:

3

I'm trying to set up unit testing for my iPhone application. I followed the Apple Unit Testing documentation through and that woked fine, but as soon as I added another class in to that test, I get the following error:

  "_OBJC_CLASS_$_RootViewController", referenced from:
      __objc_classrefs__DATA@0 in AppDelegateTests.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

The application itself is a basic navigation app with Core data for data storage.

The unit test is as follows:

#import <SenTestingKit/SenTestingKit.h>
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

#import "HSStabilityAppAppDelegate.h"
#import "RootViewController.h"
@interface AppDelegateTests : SenTestCase {
 HSStabilityAppAppDelegate *appDelegate;
}
@end


@implementation AppDelegateTests
// all code under test must be linked into the Unit Test bundle


#pragma mark -
#pragma mark Set up and tearDown

#if APPLICATION_TESTS
- (void) setUp {
 appDelegate = (HSStabilityAppAppDelegate *)[[UIApplication sharedApplication] delegate];
 STAssertNotNil(appDelegate, @"Cannot find the application delegate.");
}

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

#else

#endif


#pragma mark -
#pragma mark Tests

#if APPLICATION_TESTS

- (void) testRootViewIsOnTop {
 id topViewControllerClass = [[appDelegate.navigationController topViewController] class];
 id rootViewControllerClass = [RootViewController class];
 STAssertEquals(topViewControllerClass, rootViewControllerClass, @"Root view controller was not the top class");
}

#endif

@end

If I comment out the id rootViewControllerClass line then the program links correctly. Also, this only occurs when building against the device target, I don't have any problems if building against the simulator (probably given that application tests don't work on the simulator).

Can anyone help solve this basic and very infuriating problem?

+1  A: 

I believe I've found the answer. The problem is that whilst the compiler can see the definitions, it isn't looking in the right place when linking them up and so throws those errors. Therefore, if we move the class name resolution to runtime, we can get round all this.

Instead of:NSManagedObject use: NSClassFromString("@NSManagedObject")

This goes for pretty much all classes defined within.

If anyone can tell me how to make it work at compile time I'd still be very grateful.

Steve Workman
You need to make sure that the source code for your added class is included in your test target. Under Targets>{your_test_target}>Compile Sources, you should see your class's implementation. E.g. if you have "#import MyClass.h", make sure you have "MyClass.m" under "Compile sources" for your test target.
Ronnie Liew
A: 

I'm having the exactly same problem with my project. The Apple example works fine, other examples from the web work fine, too. Only my own project doesn't work.

Have you found any other solution to this than the one you've given yourself (the one using NSClassFromString)? Because neither do I think that that solution is very elegant.

No, I didn't get around it (I simply stopped trying to write application unit tests).I've not tried the 4.0 beta though that has promised some new unit testing features, maybe that has the answer
Steve Workman
+1  A: 

I also followed Apple's iPhone Unit Testing Applications document and saw a linking error similar to the one described in the question when trying to unit test one of my classes.

Looks like any class referenced in your unit test class and so being run from your test target also needs to be added to that test target. To do this, you would right click your RootViewController class and click 'Get Info' (Cmd-i shortcut). On the targets pane, make sure your unit test target (e.g. 'LogicTests', if you've followed the naming in that document) is checked.

Now that class will be compiled with your tests and should be available to your unit test. To double check, expand the 'Targets/LogicTests/Compile Resources' node in the 'Groups & Files' browser on the left. This lists all the class files available when building the target and should now include your unit test class together with your class under test.

(Note that you'll need to similarly select all appropriate targets when you create a new application or test class - on the same page of the 'New File...' window when you name the file).

(I'm using XCode 3.2.3 & OS 4.0, by the way).

Martin Dow