Ios 如何将核心数据用于主应用程序和单元测试?
当我尝试将核心数据与Ios 如何将核心数据用于主应用程序和单元测试?,ios,swift,unit-testing,core-data,Ios,Swift,Unit Testing,Core Data,当我尝试将核心数据与nsimemorystoretype一起用于单元测试时,我总是会遇到以下错误: Failed to find a unique match for an NSEntityDescription to a managed object subclass 这是我创建核心数据堆栈的对象: public enum StoreType { case sqLite case binary case inMemory ................. }
nsimemorystoretype
一起用于单元测试时,我总是会遇到以下错误:
Failed to find a unique match for an NSEntityDescription to a managed object subclass
这是我创建核心数据堆栈的对象:
public enum StoreType {
case sqLite
case binary
case inMemory
.................
}
public final class CoreDataStack {
var storeType: StoreType!
public init(storeType: StoreType) {
self.storeType = storeType
}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Transaction")
container.loadPersistentStores(completionHandler: { (description, error) in
if let error = error {
fatalError("Unresolved error \(error), \(error.localizedDescription)")
} else {
description.type = self.storeType.type
}
})
return container
}()
public var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
public func reset() {
for store in persistentContainer.persistentStoreCoordinator.persistentStores {
guard let url = store.url else { return }
try! persistentContainer.persistentStoreCoordinator.remove(store)
try! FileManager.default.removeItem(at: url)
}
}
}
这就是我在单元测试项目中使用它的方式:
class MyTests: XCTestCase {
var context: NSManagedObjectContext!
var stack: CoreDataStack!
override func setUp() {
stack = CoreDataStack(storeType: .inMemory)
context = stack.context
}
override func tearDown() {
stack.reset()
context = nil
}
}
从我所读到的内容来看,这似乎与我所面临的问题相同,我必须在每次测试后清理一切,我(认为)我正在做的事情
我打扫得不对吗?还有其他方法吗?应用程序中是否初始化了
CoreDataStack
类?例如,在AppDelegate
类中?当单元测试运行时,它将在测试运行之前的某个时间初始化AppDelegate
。我相信这是为了让您的测试能够调用应用程序中的任何内容,以便按照@testable import MyApp
行进行测试。如果您正在通过AppDelegate
和MyTests
初始化核心数据堆栈,则将加载两次核心数据堆栈
需要注意的是,拥有两个或多个
NSPersistentContainer
实例意味着两个或多个NSManagedObjectModel
实例将加载到内存中,这就是问题的原因。这两个模型在运行时都提供了附加的NSManagedObject
子类。当您尝试使用其中一个子类时,运行时不知道使用哪一个子类(即使它们是相同的,它只会看到它们具有相同的名称)。我认为如果NSManagedObjectModel
能够处理这种情况会更好,但是目前由开发人员来确保加载的实例不会超过一个。我知道这个问题很老,但是,我最近遇到了这个问题,在其他地方没有找到答案
基于@JamesBedford的答案,设置核心数据堆栈的一种方法是:
CoreDataStack
实例。不要在测试目标中创建新实例。在你的应用程序target中,你可以像James建议的那样使用单例。或者,如果要在AppDelegate中保留对核心数据堆栈的强引用并在启动时进行初始化,请在应用程序目标中提供一个方便的静态属性,以便从测试目标访问。比如:“persistent\u store\u type”
,value=“in\u memory”
。然后,在运行时,在CoreDataStack
初始化器中,可以使用ProcessInfo
检查此环境变量从这里开始,您的测试目标现在将使用.inMemory持久存储类型,而不会创建SQLLite存储。您甚至可以添加一个单元测试断言:)现在我只是在单元测试中使用CoreDataStack,仅此而已。但我确实相信NSManagedObjectModel是如何被多次创建的。它总是在第二个单元测试中崩溃,第一个单元测试运行良好。因此,我认为答案是确保NSManagedObjectModel不会被多次创建。您是否曾尝试在应用程序中设置一次
CoreDataStack
,然后从测试中访问它(而不是在测试中设置)?是的,行为是相同的,您只希望有1个CoreDataStack
实例存在,正确的?你试过让它成为单身汉吗?您可以在init
方法中放置一个断点,并查看每个实例创建的确切位置。
extension CoreDataStack
static var shared: CoreDataStack {
(UIApplication.shared.delegate as! AppDelegate).stack
}
}
final class CoreDataStack {
let storeType: StoreType
init() {
if ProcessInfo.processInfo.environment["persistent_store_type"] == "in_memory" {
self.storeType = .inMemory
} else {
self.storeType = .sqlLite
}
}
}