首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >NSPersistentContainer & UnitTests与iOS10

NSPersistentContainer & UnitTests与iOS10
EN

Stack Overflow用户
提问于 2017-04-05 12:55:20
回答 2查看 992关注 0票数 5

我有一个问题,我的核心数据设置单元测试。

我在我的AppDelegate中使用默认/新的核心数据堆栈设置

代码语言:javascript
运行
复制
class AppDelegate: UIResponder, UIApplicationDelegate {
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "GenericFirstFinder")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
}

为了进行测试,我有一个自定义函数来创建托管上下文。

代码语言:javascript
运行
复制
func setupInMemoryManagedObjectContext() -> NSManagedObjectContext {
    let container = NSPersistentContainer(name: "GenericFirstFinder")

    let description = NSPersistentStoreDescription()
    description.type = NSInMemoryStoreType
    container.persistentStoreDescriptions = [description]

    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })

    return container.viewContext
}

我有一个扩展来查找给定谓词的第一项。

代码语言:javascript
运行
复制
extension NSManagedObject {
    class func first(with predicate: NSPredicate?, in context: NSManagedObjectContext) throws -> Self? {
        return try _first(with: predicate, in: context)
    }

    fileprivate class func _first<T>(with predicate: NSPredicate?, in context: NSManagedObjectContext) throws -> T? where T: NSFetchRequestResult, T: NSManagedObject {
        let fetchRequest = self.fetchRequest()
        fetchRequest.fetchLimit = 1
        fetchRequest.predicate = predicate
        let results = try context.fetch(fetchRequest)
        return results.first as? T
    }
}

这是问题所在。

此测试通过:

代码语言:javascript
运行
复制
func testExample() {
    let context = setupInMemoryManagedObjectContext()
    let entity = try! Entity.first(with: nil, in: context)
    XCTAssertEqual(entity, nil)
}

但是如果persistentContainer加载是在didFinishLaunchingWithOptions中触发的

代码语言:javascript
运行
复制
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let context = persistentContainer.viewContext // end of laziness
    return true
}

然后,我得到以下错误

失败:捕捉到"NSInvalidArgumentException“、"executeFetchRequest:error:不是有效的NSFetchRequest”。

错误具体来自于_first()中的这一行

代码语言:javascript
运行
复制
let fetchRequest = self.fetchRequest() // fetchRequest  is <uninitialized> in that case

但是,如果我首先修改测试以创建一个实体,那么测试再次正常运行(当然,XCTAssertEqual不同于…)。:

代码语言:javascript
运行
复制
func testExample() {
    let context = setupInMemoryManagedObjectContext()
    let firstEntity = Entity(context: context)
    let entity = try! Entity.first(with: nil, in: context)
    XCTAssertEqual(entity, firstEntity)
}

因此,由于某种原因,创建一个新的实体(而不保存上下文)似乎会使事情恢复正常。

我想我的测试堆栈设置已经搞砸了,但我还没有弄清楚原因。你知道发生了什么事吗?有什么正确的安排方式吗?

我使用的是Xcode 8.3、SWIFT3.1,部署目标是iOS 10.3。

EN

回答 2

Stack Overflow用户

发布于 2017-04-09 07:43:54

看起来这是单元测试目标中的核心数据问题(可能在使用泛型时也是如此)。我在工作中也遇到过,这是我们的解决方案:

代码语言:javascript
运行
复制
func fetchObjects<Entity: NSManagedObject>(type: Entity.Type,
                  predicate: NSPredicate? = nil,
                  sortDescriptors: [NSSortDescriptor]? = nil,
                  fetchLimit: Int? = nil) throws -> [Entity] {

    /*
     NOTE: This fetch request is constructed this way, because there is a compiler error
     when using type.fetchRequest() or Entity.fetchRequest() only in the test target, which
     makes the fetchRequest initialize to nil and crashes the fetch at runtime.
     */
    let name = String(describing: type)
    let fetchRequest: NSFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
票数 3
EN

Stack Overflow用户

发布于 2017-04-07 19:57:32

我不熟悉类函数fetchRequest,直到查找这个问题才知道它的存在。

抬头看 一些 参考文献,但不得不承认我仍然不明白发生了什么。

最近,我一直在使用Xcode创建的自动生成的类,每个实体都包含一个fetchRequest方法,如下所示:

代码语言:javascript
运行
复制
@nonobjc public class func fetchRequest() -> NSFetchRequest<User> {
    return NSFetchRequest<Entity>(entityName: "Entity")
}

使用该格式,我将_first函数中的一行更改为:

代码语言:javascript
运行
复制
fileprivate class func _first<T>(with predicate: NSPredicate?, in context: NSManagedObjectContext) throws -> T? where T: NSFetchRequestResult, T: NSManagedObject {

    // let fetchRequest = self.fetchRequest()
    // (note that this wouldn't work if the class name and entity name were not the same)
    let fetchRequest = NSFetchRequest<T>(entityName: T.entity().managedObjectClassName)

    fetchRequest.fetchLimit = 1
    fetchRequest.predicate = predicate
    let results = try context.fetch(fetchRequest)
    return results.first
}

对我来说,这可以防止错误--我发这篇文章是为了看看它是否对你有帮助,但我需要进一步研究一下,才能理解为什么会这样做。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43231873

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档