Nic*_*hrn 27 core-data nsmanagedobject nsmanagedobjectcontext
我正在创建一个允许我使用Core Data的框架.在框架的测试目标中,我已经配置了一个名为的数据模型MockModel.xcdatamodeld.它包含一个名为MockManaged具有单个Date属性的实体.
所以我可以测试我的逻辑,我正在创建一个内存存储.当我想验证我的保存逻辑时,我创建了一个内存存储的实例并使用它.但是,我一直在控制台中获得以下输出:
2018-08-14 20:35:45.340157-0400 xctest[7529:822360] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'LocalPersistenceTests.MockManaged' so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'LocalPersistenceTests.MockManaged' so +entity is unable to disambiguate.
2018-08-14 20:35:45.340558-0400 xctest[7529:822360] [error] warning: 'MockManaged' (0x7f986861cae0) from NSManagedObjectModel (0x7f9868604090) claims 'LocalPersistenceTests.MockManaged'.
CoreData: warning: 'MockManaged' (0x7f986861cae0) from NSManagedObjectModel (0x7f9868604090) claims 'LocalPersistenceTests.MockManaged'.
2018-08-14 20:35:45.340667-0400 xctest[7529:822360] [error] warning: 'MockManaged' (0x7f986acc4d10) from NSManagedObjectModel (0x7f9868418ee0) claims 'LocalPersistenceTests.MockManaged'.
CoreData: warning: 'MockManaged' (0x7f986acc4d10) from NSManagedObjectModel (0x7f9868418ee0) claims 'LocalPersistenceTests.MockManaged'.
2018-08-14 20:35:45.342938-0400 xctest[7529:822360] [error] error: +[LocalPersistenceTests.MockManaged entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[LocalPersistenceTests.MockManaged entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
Run Code Online (Sandbox Code Playgroud)
下面是我用来创建内存存储的对象:
class MockNSManagedObjectContextCreator {
// MARK: - NSManagedObjectContext Creation
static func inMemoryContext() -> NSManagedObjectContext {
guard let model = NSManagedObjectModel.mergedModel(from: [Bundle(for: self)]) else { fatalError("Could not create model") }
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
do {
try coordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
} catch {
fatalError("Could not create in-memory store")
}
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.persistentStoreCoordinator = coordinator
return context
}
}
Run Code Online (Sandbox Code Playgroud)
以下是构成我的MockManaged实体的内容:
class MockManaged: NSManagedObject, Managed {
// MARK: - Properties
@NSManaged var date: Date
}
Run Code Online (Sandbox Code Playgroud)
以下是我的意思XCTestCase:
class Tests_NSManagedObjectContext: XCTestCase {
// MARK: - Object Insertion
func test_NSManagedObjectContext_InsertsManagedObject_WhenObjectConformsToManagedProtocol() {
let context = MockNSManagedObjectContextCreator.inMemoryContext()
let changeExpectation = expectation(forNotification: .NSManagedObjectContextObjectsDidChange, object: context, handler: nil)
let object: MockManaged = context.insertObject()
object.date = Date()
wait(for: [changeExpectation], timeout: 2)
}
// MARK: - Saving
func test_NSManagedObjectContext_Saves_WhenChangesHaveBeenMade() {
let context = MockNSManagedObjectContextCreator.inMemoryContext()
let saveExpectation = expectation(forNotification: .NSManagedObjectContextDidSave, object: context, handler: nil)
let object: MockManaged = context.insertObject()
object.date = Date()
do {
try context.saveIfHasChanges()
} catch {
XCTFail("Expected successful save")
}
wait(for: [saveExpectation], timeout: 2)
}
func test_NSManagedObjectContext_DoesNotSave_WhenNoChangesHaveBeenMade() {
let context = MockNSManagedObjectContextCreator.inMemoryContext()
let saveExpectation = expectation(forNotification: .NSManagedObjectContextDidSave, object: context, handler: nil)
saveExpectation.isInverted = true
do {
try context.saveIfHasChanges()
} catch {
XCTFail("Unexpected error: \(error)")
}
wait(for: [saveExpectation], timeout: 2)
}
}
Run Code Online (Sandbox Code Playgroud)
我在做什么导致我的测试中的错误?
Fab*_*ian 17
使用不应再发生这种情况NSPersistent[CloudKit]Container(name: String),因为它似乎现在已自动缓存模型(Swift 5.1,Xcode11,iOS13 / MacOS10.15)。
NSPersistentContainer/NSPersistentCloudKitContainer 确实有两个构造函数:
第一个只是一个便利的初始化程序,第二个调用从磁盘加载的模型。问题在于,NSManagedObjectModel从磁盘中两次在同一磁盘中加载同一磁盘会app/test invocation导致上述错误,因为每次加载模型都会导致外部注册调用,而该调用会再次在同一磁盘上打印两次错误app/test invocation。而且init(name: String) 还不足以缓存模型。
因此,如果您想多次加载一个容器,则必须加载NSManagedObjectModel一次并将其存储在一个属性中,然后在每次init(name:managedObjectModel:)调用时使用。
import Foundation
import SwiftUI
import CoreData
import CloudKit
class PersistentContainer {
private static var _model: NSManagedObjectModel?
private static func model(name: String) throws -> NSManagedObjectModel {
if _model == nil {
_model = try loadModel(name: name, bundle: Bundle.main)
}
return _model!
}
private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel {
guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else {
throw CoreDataError.modelURLNotFound(forResourceName: name)
}
guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
throw CoreDataError.modelLoadingFailed(forURL: modelURL)
}
return model
}
enum CoreDataError: Error {
case modelURLNotFound(forResourceName: String)
case modelLoadingFailed(forURL: URL)
}
public static func container() throws -> NSPersistentCloudKitContainer {
let name = "ItmeStore"
return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
}
}
Run Code Online (Sandbox Code Playgroud)
加载核心数据有点神奇,从磁盘加载模型并使用它意味着它注册了某些类型。第二次加载尝试再次注册该类型,这显然告诉您已经为该类型注册了某些内容。
您只能加载一次核心数据,并在每次测试后清理该实例。清理意味着删除每个对象实体,然后保存。有一些功能可以为您提供所有实体,然后您可以提取和删除它们。批量删除在InMemory中不可用,尽管它是按对象管理的对象。
(可能更简单)的替代方法是一次加载模型,将其存储在某个位置,然后在每次NSPersistentContainer调用时重用该模型,它具有一个构造函数,可以使用给定的模型来代替从磁盘上再次加载模型。
Kam*_*tka 17
在使用内存存储的单元测试环境中,最终会加载两个不同的模型:
这会导致问题,因为显然会+ [NSManagedObjectModel entity]查看所有可用模型以查找NSManagedObject的匹配实体.由于它找到两个模型,它会抱怨.
解决方案是将对象插入上下文中insertNewObjectForEntityForName:inManagedObjectContext:.这将考虑上下文(以及因此,上下文的模型)来查找实体模型,并因此将其搜索限制为单个模型.
对我来说,似乎是NSManagedObject init(managedObjectContext:)方法中的一个错误似乎依赖于+[NSManagedObject entity]而不是依赖于上下文的模型.
Lor*_*lor 13
[错误]警告:多个 NSEntityDescriptions 声明...
此警告是由多个声称为同一托管对象子类的托管对象模型引起的。
在核心数据单元测试的背景下,这并不是什么大问题,因为我们知道它不会破坏任何东西。但是,通过添加静态托管对象模型并将其用于我们创建的每个持久容器,也可以轻松摆脱警告消息。xcdatamodeld下面的代码片段中是核心数据模型文件的文件名。
下面的代码片段基于 Xcode 生成的 Core Data 模板代码
public class PersistentContainer: NSPersistentCloudKitContainer {}
class PersistenceController {
static let shared = PersistenceController()
static var managedObjectModel: NSManagedObjectModel = {
let bundle = Bundle(for: PersistenceController.self)
guard let url = bundle.url(forResource: "xcdatamodeld", withExtension: "momd") else {
fatalError("Failed to locate momd file for xcdatamodeld")
}
guard let model = NSManagedObjectModel(contentsOf: url) else {
fatalError("Failed to load momd file for xcdatamodeld")
}
return model
}()
let container: PersistentContainer
init(inMemory: Bool = false) {
container = PersistentContainer(name: "xcdatamodeld", managedObjectModel: Self.managedObjectModel)
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
Run Code Online (Sandbox Code Playgroud)
正如@Kamchatka指出的那样,显示警告是因为NSManagedObject init(managedObjectContext:)已使用。使用NSManagedObject initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context消除警告。
如果您不想在测试中使用更高版本的构造函数,则只需NSManagedObject在测试目标中简单地创建override默认行为的扩展即可:
import CoreData
public extension NSManagedObject {
convenience init(usedContext: NSManagedObjectContext) {
let name = String(describing: type(of: self))
let entity = NSEntityDescription.entity(forEntityName: name, in: usedContext)!
self.init(entity: entity, insertInto: usedContext)
}
}
Run Code Online (Sandbox Code Playgroud)
尝试进行以下目标的CoreData相关单元测试时遇到了此问题:
作为Fabian的答案,此问题的根本原因是managedObjectModel多次加载。但是,托管对象模型的加载可能存在多个位置:
setUpXCTestCase子类调用都会尝试重新创建NSPersistentContainer因此,解决此问题有两个方面。
您可以添加一个underTesting标志来确定是否设置它。
managedObjectModel在所有单元测试中仅加载一次我使用一个静态变量,managedObjectModel并将其用于重新创建内存中的NSPersistentContainer。
摘录如下:
class UnitTestBase {
static let managedObjectModel: NSManagedObjectModel = {
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: UnitTestBase.self)])!
return managedObjectModel
}()
override func setUp() {
// setup in-memory NSPersistentContainer
let storeURL = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("store")
let description = NSPersistentStoreDescription(url: storeURL)
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
description.shouldAddStoreAsynchronously = false
description.type = NSInMemoryStoreType
let persistentContainer = NSPersistentContainer(name: "DataModel", managedObjectModel: UnitTestBase.managedObjectModel)
persistentContainer.persistentStoreDescriptions = [description]
persistentContainer.loadPersistentStores { _, error in
if let error = error {
fatalError("Fail to create CoreData Stack \(error.localizedDescription)")
} else {
DDLogInfo("CoreData Stack set up with in-memory store type")
}
}
inMemoryPersistentContainer = persistentContainer
}
}
Run Code Online (Sandbox Code Playgroud)
上面的内容足以解决您在单元测试中发生的问题。
| 归档时间: |
|
| 查看次数: |
7238 次 |
| 最近记录: |