Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/93.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何将核心数据用于主应用程序和单元测试?_Ios_Swift_Unit Testing_Core Data - Fatal编程技术网

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中保留对核心数据堆栈的强引用并在启动时进行初始化,请在应用程序目标中提供一个方便的静态属性,以便从测试目标访问。比如:
  • 在Xcode中将环境变量添加到测试方案中。转到Xcode>编辑方案>测试>参数>环境变量。添加一个新的名称-值对,例如:name=
    “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
            }
        }
        
    }