【问题标题】:How can I unit test a Core Data migration?如何对 Core Data 迁移进行单元测试?
【发布时间】:2012-08-11 03:59:48
【问题描述】:

我正在使用自定义实体迁移策略为我的迁移构建一个映射模型,我真的很想为此迁移构建一些单元测试。当我运行应用程序时,迁移似乎正常工作,但是当我通过单元测试运行迁移时,我的 NSEntityMigrationPolicy 子类方法根本没有被调用。

我正在使用 Xcode 的内置 OCUnit 框架。

我的测试代码:

- (void)test1to2Migration_appIdentifierMoved {
  [self createVersion1Store];

  // TODO Perform migration
  NSManagedObjectModel *version1Model = [self version1Model];
  NSManagedObjectModel *version2Model = [self version2Model];

  NSError *error = nil;
  NSMappingModel *mappingModel = [NSMappingModel
      inferredMappingModelForSourceModel:version1Model
      destinationModel:version2Model error:&error];
  STAssertNotNil(mappingModel, @"Error finding mapping model: %@", error);

  NSMigrationManager *migrationManager =
      [[[NSMigrationManager alloc]
        initWithSourceModel:version1Model
        destinationModel:version2Model]
       autorelease];

  BOOL migrationSucceeded =
      [migrationManager migrateStoreFromURL:self.version1StoreURL
           type:NSSQLiteStoreType
           options:nil
           withMappingModel:mappingModel
           toDestinationURL:self.version2StoreURL
           destinationType:NSSQLiteStoreType
           destinationOptions:nil
           error:&error];
  STAssertTrue(migrationSucceeded, @"Error migrating store: %@", error);

  // TODO Verify appIdentifier is moved from Project to its Tests

  [self deleteTempStores];
}

我的映射模型指定了一个自定义的 NSEntityMigrationPolicy,它定义了 -createRelationshipsForDestinationInstance:entityMapping:manager:error: 方法,但我的策略从未从单元测试中调用。当我运行迁移时,模型被修改为新版本——预期的属性显示在正确的位置。

有什么想法可以让我的迁移策略在单元测试中发挥作用吗?

【问题讨论】:

    标签: ios unit-testing core-data ocunit core-data-migration


    【解决方案1】:

    斯威夫特 3

    为您的模型文件名替换变量 modelNamemodelNameVersionFormatString

    import XCTest
    import CoreData
    
    class RCCoreDataMigrationTests: XCTestCase {
    
        private let storeType = NSSQLiteStoreType
        private let modelName = "Model"
        private let modelNameVersionFormatString = "Model-%@"
    
        private func storeURL(_ version: String) -> URL? {
            let storeURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())\(version).sqlite" )
            return storeURL
        }
    
        private func createObjectModel(_ version: String) -> NSManagedObjectModel? {
            let bundle = Bundle.main
            let managedObjectModelURL = bundle.url(forResource: modelName, withExtension: "momd")
            let managedObjectModelURLBundle = Bundle(url: managedObjectModelURL!)
            let modelVersionName = String(format: modelNameVersionFormatString, version)
            let managedObjectModelVersionURL = managedObjectModelURLBundle!.url(forResource: modelVersionName, withExtension: "mom")
            return NSManagedObjectModel(contentsOf: managedObjectModelVersionURL!)
        }
    
        private func createStore(_ version: String) -> NSPersistentStoreCoordinator {
            let model = createObjectModel(version)
            let storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model!)
            try! storeCoordinator.addPersistentStore(ofType: storeType,
                                                     configurationName: nil,
                                                     at: storeURL(version),
                                                     options: nil)
            return storeCoordinator
        }
    
        private func migrateStore(fromVersionMOM: String, toVersionMOM: String) {
            let store = createStore(fromVersionMOM)
            let nextVersionObjectModel = createObjectModel(toVersionMOM)!
            let mappingModel = NSMappingModel(from: [Bundle.main], forSourceModel: store.managedObjectModel, destinationModel: nextVersionObjectModel)!
            let migrationManager = NSMigrationManager(sourceModel: store.managedObjectModel, destinationModel: nextVersionObjectModel)
            do {
                try migrationManager.migrateStore(from: store.persistentStores.first!.url!,
                                                  sourceType: storeType,
                                                  options: nil,
                                                  with: mappingModel,
                                                  toDestinationURL: storeURL(toVersionMOM)!,
                                                  destinationType: NSSQLiteStoreType,
                                                  destinationOptions: nil)
            } catch {
                print("Error: \(error)")
                XCTAssertNil(error)
            }
            try! FileManager.default.removeItem(at: storeURL(toVersionMOM)!)
            try! FileManager.default.removeItem(at: storeURL(fromVersionMOM)!)
        }
    
        func testMigratingStores() {
            migrateStore(fromVersionMOM: "1486", toVersionMOM: "1487")
        }
    }
    

    【讨论】:

    • 我的映射模型总是返回 nil。你知道为什么吗 ? ://
    • @chr0x 我想问题是您的测试包与包含您的数据模型的包不同。我有同样的问题,但还不知道如何解决。
    【解决方案2】:

    原来问题出在一行

    NSMappingModel *mappingModel = [NSMappingModel
      inferredMappingModelForSourceModel:version1Model
      destinationModel:version2Model error:&error];
    

    如果我把它改成

    NSMappingModel *mappingModel = [NSMappingModel
      mappingModelFromBundles:@[[NSBundle bundleForClass:[MyTestClass class]]]
      forSourceModel:version1Model destinationModel:version2Model];
    

    然后测试正常运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-04
      • 1970-01-01
      • 2011-06-15
      • 1970-01-01
      • 2012-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多