Модульный тест не может найти файл модели Core Data

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

К сожалению, модульный тест продолжает возвращать значение null:

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

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

Спасибо, Лютер


person Luther Baker    schedule 14.04.2011    source источник
comment
Спасибо, что упомянули, что вам необходимо добавить свою модель данных в список скомпилированных источников тестового проекта. Вот чего мне не хватало.   -  person d512    schedule 18.04.2014


Ответы (5)


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

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

Пример:

- (void)testBundleLocation
{
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSURL *url = [bundle URLForResource:@"myDataModel" withExtension:@"momd"];
    ...
}
person Till    schedule 18.04.2012
comment
@LutherBaker Хотя на данный момент это кажется лучшим обходным решением среди всех решений, я не думаю, что это правильный ответ. Модульное тестирование не должно требовать изменения исходного кода приложения. Итак, пока вы получаете доступ к пакету в тестовом примере, выполнение bundleForClass: кажется нормальным, но если у вас есть код приложения, который выполняет [NSBundle mainBundle] (достаточно распространенная идиома, а не устаревшая, насколько мне известно), этот класс не может быть протестирован. - person Manav; 31.01.2013
comment
Большое спасибо. Потерял два полных дня, пытаясь понять это! - person user798719; 20.11.2013

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

Тогда исправленное решение выглядит так:

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

Спасибо

person Luther Baker    schedule 14.04.2011
comment
Этот ответ также хорош, потому что он предоставляет всю информацию, необходимую для понимания и устранения проблемы. - person Ben G; 24.01.2013

Была аналогичная проблема, я решил ее с помощью фреймворка 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];
}
person user966697    schedule 26.02.2014

Этот метод получит ваш пакет из любой цели. Однако для каждой цели, которую вы добавляете, вы должны вручную добавить идентификатор пакета 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;
}
person Jano    schedule 03.10.2011

Моя проблема действительно заключалась в неправильном комплекте! Поскольку я пытался использовать базу данных из/внутри Framework, я «просто» должен загрузить базу данных из соответствующего 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")

И вуаля!

person Kevin Delord    schedule 03.01.2018