NSPersistentContainer 将加载到应用程序中,不会加载到测试目标中 [英] NSPersistentContainer will load in app, won't load in test target

查看:59
本文介绍了NSPersistentContainer 将加载到应用程序中,不会加载到测试目标中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题首先出现在我的应用程序项目中,但它使我感到困惑,以至于我试图通过切换到遇到相同问题的Swift Package来简化事情.

This problem first appeared in my app project, but it's baffled me enough that I tried to simplify things by switching to a Swift Package where I get the same issue.

在我的项目中,我有一个名为"Model.xcdatamodeld"的数据模型文件.(尽管无论名称如何,都会出现相同的问题).当我在运行的应用程序中调用以下init方法时,一切运行正常:

In my project, I have a data model file called "Model.xcdatamodeld" (though the same problem appears no matter what the name). When I call the following init method in my running app, everything runs fine:

    init(title: String, then completion: @escaping ()->()) {
    self.container = NSPersistentContainer(name: title)
    
    container.loadPersistentStores { description, error in
        // don't worry about retaining self, this object lives the whole life of the app
        
        print(description)
        
        if let error = error {
            print("Error loading persistent store named \(title): \(error.localizedDescription)")
            return
        }
                    
        completion()
    }
}

没有打印错误,调用了完成块,在应用程序的剩余生命中,我可以访问持久性容器的viewContext.

No error is printed, the completion block is called, and I can access the persistent container's viewContext for the rest of the life of the app.

但是当我在测试目标中运行相同的代码时,我会得到非常不同的行为.永远不会输入闭包,而当我尝试访问容器的viewContext时,它为nil.

But when I run the same code in my test target, I get very different behavior. The closure is never entered and when I try to access the container's viewContext, it's nil.

我将测试简化为更简单的内容:

I've reduced the test to something that's even simpler:

    func testLoadingMomD() {
    
    let expectation = XCTestExpectation(description: "core data model is set up")

    let container = NSPersistentContainer(name: "Model")
    container.loadPersistentStores { description, error in
        if let error = error {
            print(error.localizedDescription)
        }
        
        print(description)
        
        expectation.fulfill()

    }
    
    wait(for: [expectation], timeout: 10.0)

}

运行此测试时,我将在控制台中获得以下输出:

when I run this test, I get the following output in the console:

Test Case '-[GoldfishCoreDataTests.GoldfishCoreDataTests testLoadingMomD]' started.
2020-12-03 22:47:07.695319-0500 xctest[2548:72372] [error] error:  Failed to load model named Model
CoreData: error:  Failed to load model named Model
/Users/joseph/Documents/QuickScheduling/GoldfishCoreData/GoldfishCoreData/Tests/GoldfishCoreDataTests/GoldfishCoreDataTests.swift:23: error: -[GoldfishCoreDataTests.GoldfishCoreDataTests testLoadingMomD] : Asynchronous wait failed: Exceeded timeout of 10 seconds, with unfulfilled expectations: "core data model is set up".
Test Case '-[GoldfishCoreDataTests.GoldfishCoreDataTests testLoadingMomD]' failed (10.021 seconds).
Test Suite 'GoldfishCoreDataTests' failed at 2020-12-03 22:47:17.714.
     Executed 1 test, with 1 failure (0 unexpected) in 10.021 (10.021) seconds
Test Suite 'GoldfishCoreDataTests.xctest' failed at 2020-12-03 22:47:17.715.
     Executed 1 test, with 1 failure (0 unexpected) in 10.021 (10.022) seconds
Test Suite 'All tests' failed at 2020-12-03 22:47:17.715.
     Executed 1 test, with 1 failure (0 unexpected) in 10.021 (10.022) seconds
Program ended with exit code: 1

如果我在闭包内的任何地方放置一个断点,它将永远不会触发.

If I put a breakpoint anywhere within the closure, it never fires.

当然,我已经验证了Model.xcdatamodeld文件在适当的目标中,甚至删除了Model.xcdatamodeld并重新创建了该文件.正如我所说的,这发生在我的应用程序项目中,并且在单独的Swift程序包中发生,该程序包仅包含此测试代码和Model.xcdatamodeld文件.

Of course, I've verified that the Model.xcdatamodeld file is in the appropriate target, and I've even deleted the Model.xcdatamodeld and recreated it with no change. As I said, this happens in my app project and in a separate Swift Package that only contains this test code and the Model.xcdatamodeld file.

从长远来看,我在这个项目中同时拥有一个iOS目标和一个macOS目标,无论我测试哪个目标,我都会得到相同的行为.

For good measure, I have both an iOS and a macOS target in this project, and I get the same behavior no matter which target I test.

在XCTest目标中使用NSPersistentContainer只是不可能吗?

Is it just impossible to use NSPersistentContainer within an XCTest target?

推荐答案

因此,简短的答案是NSPersistentContainer仅在主捆绑包中查找其模型文件,除非另行告知.尝试在不是应用程序目标(例如测试目标)的目标中使用它,并且找不到模型文件.您必须明确告诉它要使用的模型文件.因此,您必须使用Bundle来查找类型为"momd"的资源.

So the short answer is that NSPersistentContainer only looks for its model file in the main bundle unless it's told otherwise. Try to use it in a target that's not an application target (like a test target) and it won't find the model file. You have to explicitly tell it which model file to use yourself. So you have to use Bundle to find the resource, which is of type "momd".

这是我想出的:

enum LoadingError: Error {
    case doesNotExist(String)
    case corruptObjectModel(URL)
}

init?(title: String, then completion: @escaping (Error?)->()) {
    
    guard let modelURL = Bundle(for: type(of: self)).url(forResource: title, withExtension: "momd") else {
        completion(LoadingError.doesNotExist(title))
        return nil
    }

    guard let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) else {
        completion(LoadingError.corruptObjectModel(modelURL))
        return nil
    }
    
    self.container = NSPersistentContainer(name: title, managedObjectModel: managedObjectModel)
    
    container.loadPersistentStores { description, error in
        // don't worry about retaining self, this object lives the whole life of the app
                    
        if let error = error {
            print("Error loading persistent store named \(title): \(error.localizedDescription)")
            DispatchQueue.main.async {
                completion(error)
            }
            return
        }
                               
        completion(nil)
    }
}

顺便说一句,显然您可以通过将NSPersistenContainer子类化来避免这种情况(请参阅 https://asciiwwdc.com/2018/sessions/224?q=nspersistentcontainer ).出于其他原因,这不是我的首选选项,但对其他人可能有用.

As an aside, apparently you can avoid this by subclassing NSPersistenContainer (see https://asciiwwdc.com/2018/sessions/224?q=nspersistentcontainer). This wasn't the preferred option for me for other reasons, but it may be useful for someone else.

这篇关于NSPersistentContainer 将加载到应用程序中,不会加载到测试目标中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆