问题
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
回答1:
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"];
...
}
回答2:
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
回答3:
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];
}
回答4:
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;
}
回答5:
My problem was indeed the wrong Bundle! As I was trying to use a database from/within a Framework I 'simply' has to load the db from the corresponding Bundle
!
Here is some code in Swift4 using MagicalRecord
:
// Load the bundle
let frameworkBundle = Bundle(for: AClassFromTheFramework.self)
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [frameworkBundle])
// Use the new `managedObjectModel` by default
MagicalRecord.setShouldAutoCreateManagedObjectModel(false)
NSManagedObjectModel.mr_setDefaultManagedObjectModel(managedObjectModel)
// Load the database
MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: "db.sqlite")
And voilà!
来源:https://stackoverflow.com/questions/5662947/unit-test-cant-find-core-data-model-file