ios - NSInMemoryStoreType 类型的核心数据忽略实体的约束

标签 ios swift unit-testing core-data

我创建了 CoreData 堆栈的模拟版本

import Foundation
import CoreData

@testable import Companion

final class MockedDatabaseStackController: DatabaseStackControllerProtocol {

    let batchRequestsAvailable: Bool = false

    private lazy var managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: type(of: self))])!

    lazy var persistentContainer: NSPersistentContainer = {
        let description = NSPersistentStoreDescription()
        description.type = NSInMemoryStoreType
        description.shouldAddStoreAsynchronously = false

        let container = NSPersistentContainer(
            name: "database",
            managedObjectModel: managedObjectModel
        )
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores { description, error in
            // Check if the data store is in memory
            precondition( description.type == NSInMemoryStoreType )

            // Check if creating container wrong
            if let error = error {
                fatalError("Create an in-mem coordinator failed \(error)")
            }
        }

        return container
    }()

    init() {
        NotificationCenter.default
            .addObserver(
                self,
                selector: #selector(didManagedObjectContextSave(notification:)),
                name: .NSManagedObjectContextDidSave,
                object: nil
            )
    }

    @objc
    private func didManagedObjectContextSave(notification: Notification) {
        DispatchQueue.main.async { [weak self] in
            self?.persistentContainer.viewContext.mergeChanges(fromContextDidSave: notification)
        }
    }
}

我用它来保存一些对象:

private func executeAndSave<T>(_ executionBlock: @escaping ((NSManagedObjectContext) throws -> T)) -> Single<T> {
    let persistentContainer = stackController.persistentContainer

    return Single.create { observer in
        persistentContainer.performBackgroundTask { context in
            do {
                context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

                jsons.forEach {
                    let mo = type.init(context: context)
                    mo.update(withGatewayResponse: $0)
                }
                try context.save()

                DispatchQueue.main.async {
                    observer(.success(result))
                }
            } catch {
                DispatchQueue.main.async {
                    observer(.error(error))
                }
            }
        }

        return Disposables.create()
    }
}

func save(jsons: [JSON], as type: GatewayObjectDeserializableAndSavable.Type) -> Single<Void> {
    if jsons.isEmpty {
        log.info("(\(type)) Nothing to save.")
        return .just(())
    }

    log.info("DatabaseHelper will save \(type)")

    return executeAndSave { context in
        jsons.forEach {
            let mo = type.init(context: context)
            mo.update(withGatewayResponse: $0)
        }
    }
}

// Example of usage:
databaseHelper.save(jsons: jsons, as: Herd.self)

我在数据库模型中创建了约束,例如: enter image description here

但它不起作用。对象在数据库中重复。 enter image description here

请注意,在我使用此 CoreData 堆栈的主要目标中一切正常:

final class DatabaseStackController: DatabaseStackControllerProtocol {

    // singleton
    static let controller = DatabaseStackController()

    private static let kDatabaseName = "database"
    let persistentContainer: NSPersistentContainer = DatabaseStackController.buildDatabaseStack(onComplete: nil)

    let batchRequestsAvailable: Bool = true

    private init() {
        addNSMangedObjectContextObservers()
    }

    private static func buildDatabaseStack(onComplete: (() -> Void)?) -> NSPersistentContainer {
        let container = NSPersistentContainer(name: kDatabaseName)
        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }

            onComplete?()
        }

        return container
    }
}

为什么它不起作用? NSInMemoryStoreType 不支持 CoreData 的约束吗?有可能修复它吗?

最佳答案

我认为你在 Core Data 中发现了一个 bug :(

我有一个关于 Core Data 的 Unique Constraint 的小演示项目。我确认它按预期工作 except for some exceptions ,总是将所有重复项与 SQLite 存储合并。然后,我粘贴到您的 MockedDatabaseStackController 类中,并将其 persistentContainer.viewContextNSMergeByPropertyObjectTrumpMergePolicy 集一起使用。结果:它似乎在第一个保存 操作中合并了第一组重复项,但之后就没有了。然后我切换回我的核心数据堆栈,只是我将存储类型更改为 NSInMemoryStoreType。结果:它也无法正常工作,与您的 MockedDatabaseStackController 相同。

作为 Core Data 的 SQLite 存储基础的 SQLite 数据库支持 its SQL 中的UNIQUE 约束。 .我希望有人能证明我是错的,但遗憾的是,我怀疑 Apple 使用 SQLite 的这个特性来实现 Core Data 中的 Unique Constraint 特性,但未能添加到 their documentation 中。事实上它只适用于 SQLite 存储。

我已将此提交给 Apple Bug Reporter:50725935。

至于你的测试,我认为你应该修改它来创建一个临时的 SQLite 存储。事实上,维恩图的另一侧有一些特性——内存存储支持,但 SQLite 存储支持。使用内存存储进行测试可能会在您的测试范围内留下漏洞。

关于ios - NSInMemoryStoreType 类型的核心数据忽略实体的约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56077716/

相关文章:

iphone - Phonegap 应用程序 : External URL don't open in InApp Browser of IOS

ios - 使用矩阵,加速框架,iOS

JSONSerialisation API 不同的 url 不工作

java - 如何为 StrutsTestCase 配置 pom 以便找到 web.xml?

javascript - 使用 React 测试库在 React 中为表单提交断言 prop 调用

ios - ScrollView 和堆栈 View

ios - 在 swift 中使用简单的 Ping (iOS)

swift - Mac OS 应用程序中用户选择的文件夹的读写权限?

swift - Facebook登录时FBSDKApplicationDelegate有什么用?

c# - AutoFixture重构