Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Test can't find Core Data model file

I've created a project with a Core Data model in it. The application looks for the model file (.momd) and runs just fine.

Unfortunately, the unit test keeps returning null:

NSURL *dataModelURL = [[NSBundle mainBundle] URLForResource:@"myDataModel" withExtension:@"momd"];

I can see the myDataModel.xdatamodeld folder and file in BOTH the main target and the unit testing target's Compile Sources directory - but that doesn't seem to be enough. What else am I missing in the unit test target?

Thanks, -Luther

like image 711
Luther Baker Avatar asked Apr 14 '11 12:04

Luther Baker


4 Answers

Unfortunately, a unit test target does not use the application's main bundle but it creates a special UnitTest-bundle. So if you need to use bundled resources (like a Core Data model) within your tests, you need to work around that issue.

The most simple and most flexible workaround would be using the bundleForClass: method of NSBundle within your testing code. The parameter for that method can simply be given by [self class] within your tests. That way you can reuse this code without having to adjust the bundle identifiers in multiple projects.

Example:

- (void)testBundleLocation
{
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSURL *url = [bundle URLForResource:@"myDataModel" withExtension:@"momd"];
    ...
}
like image 111
Till Avatar answered Oct 23 '22 09:10

Till


The answer has to do with the bundle. A unit test target doesn't use the 'main' bundle. It creates its own bundle which, in my case, defaulted to 'com.yourcompany.UnitTest' - straight out of the [Target]-info.plist.

The corrected solution then looks like this:

NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.yourcompany.UnitTests"];
NSURL *url = [bundle URLForResource:@"myDataModel" withExtension:@"momd"];

Thanks

like image 6
Luther Baker Avatar answered Oct 23 '22 09:10

Luther Baker


Had a similar problem, i solved it using the OCMock framework, so i did not need to change the application code

@interface TestCase()
    @property (nonatomic, strong) id bundleMock;
@end

@implementation TestCase

- (void)setUp
{
    self.bundleMock = [OCMockObject mockForClass:[NSBundle class]];
    [[[self.bundleMock stub] andReturn:[NSBundle bundleForClass:[self class]]] mainBundle];
    [super setUp];
}

- (void)tearDown
{
    [self.bundleMock stopMocking];
    [super tearDown];
}
like image 3
user966697 Avatar answered Oct 23 '22 09:10

user966697


This method will get your bundle from any target. However, for each target you add, you have to manually add the plist bundle identifier to the identifiers array, because there is no way to get it programmatically. The advantage is that you can use the same code for testing or running the application.

+(NSBundle*) getBundle 
{
    NSBundle *bundle = nil;

    // try your manually set bundles
    NSArray *identifiers = [NSArray arrayWithObjects: @"com.your.application",
                                                      @"com.your.test",
                                                      nil];
    for(NSString *bundleId in identifiers) {
        bundle = [NSBundle bundleWithIdentifier:bundleId];
        if (bundle!=nil) break;
    }

    // try the main bundle
    if (bundle==nil) bundle = [NSBundle mainBundle];

    // abort
    assert(bundle!=nil && "Missing bundle. Check the Bundle identifier on 
           the plist of this target vs the identifiers array in this class.");

    return bundle;
}
like image 2
Jano Avatar answered Oct 23 '22 10:10

Jano