Unit Test не удается найти файл модели Core Data

Я создал проект с моделью Core Data в нем. Приложение ищет файл модели (.momd) и работает просто отлично.

К сожалению, unit test возвращает значение null:

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

Я могу видеть папку и файл myDataModel.xdatamodeld в BOTH главной цели и целевом целевом сервере Compile Sources - но этого, похоже, недостаточно. Что еще мне не хватает в unit test target?

Спасибо, -Luther

Ответ 1

К сожалению, цель unit test не использует основной комплект приложения, но создает специальный пакет UnitTest. Поэтому, если вам нужно использовать связанные ресурсы (например, модель Core Data) в рамках ваших тестов, вам необходимо обойти эту проблему.

Наиболее простым и гибким решением будет использование метода bundleForClass: NSBundle в вашем тестовом коде. Параметр для этого метода может быть просто задан [self class] в рамках ваших тестов. Таким образом, вы можете повторно использовать этот код без необходимости корректировать идентификаторы пакетов в нескольких проектах.

Пример:

- (void)testBundleLocation
{
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSURL *url = [bundle URLForResource:@"myDataModel" withExtension:@"momd"];
    ...
}

Ответ 2

Ответ должен быть связан с пакетом. Цель unit test не использует "основной" комплект. Он создает свой собственный пакет, который, в моем случае, по умолчанию "com.yourcompany.UnitTest" - прямо из [Target] -info.plist.

Исправленное решение выглядит следующим образом:

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

Спасибо

Ответ 3

Имел подобную проблему, я решил ее использовать среду OCMock, поэтому мне не нужно было менять код приложения

@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

Этот метод получит ваш комплект из любой цели. Тем не менее, для каждой добавленной цели вы должны вручную добавить идентификатор связки plist в массив identifiers, потому что нет способа получить его программным путем. Преимущество состоит в том, что вы можете использовать один и тот же код для тестирования или запуска приложения.

+(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

Моя проблема была действительно неправильным Bundle! Поскольку я пытался использовать базу данных из/внутри Framework, я просто 'должен загрузить db из соответствующего Bundle!

Вот код в Swift4 с помощью 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")

И вуаля!