【问题标题】:Swift/OSX - How to give unit test Core Data access AND run the test without running the appSwift/OSX - 如何在不运行应用程序的情况下授予单元测试核心数据访问权限并运行测试
【发布时间】:2018-08-05 06:48:23
【问题描述】:

我在我非常小而简单的应用程序中使用 Core Data。它工作正常。在开始开发应用程序并使其更复杂之前,我想设置一些单元测试。要实现这一点,您必须跳过一些障碍,但我在 SE 上找到了 numerous useful answers 让我通过这些障碍。基本思想是您必须手动创建对象模型。很简单;我根据上面链接的答案之一改编了这段代码,问题解决了。

let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])!
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
try! persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)

let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator

NSEntityDescription.entity(forEntityName: "VAgentEditor", in: managedObjectContext)!

太好了。但我也想在不运行应用程序的情况下运行我的测试,只是为了测试我的低级逻辑。更多的箍要跳过。也有很多有用的answers,问题解决了。

当我尝试访问 Core Data 在没有应用程序窗口的情况下运行时,就会出现问题。天真地结合了这两个问题的解决方案,我从对NSEntityDescription.entity() 的调用中得到了 nil。我已经以我能想到的各种方式调整了上面的代码,但无济于事。如果我可以破解它,从对entity() 的调用中获得非零返回,我最终会收到无法找到VAgentEditor 的消息,或者当我尝试将上下文保存到磁盘时,我会得到一个文件看起来有些事情成功了一半—— 里面有很多东西,但没有VAgentEditor,就像我只尝试上述两种工作场景中的一种时那样。

当我尝试将单元测试指向主应用程序包时,我收到各种关于找不到包的投诉,或者单元测试无法同时使用它自己的包和应用包,以及其他一百个几天后我不记得抱怨了。

有没有办法让我的单元测试使用 CoreData,访问正确的包以获取我在 Interface Builder 中定义的类,并在不运行主应用程序的情况下运行测试?

【问题讨论】:

    标签: swift macos unit-testing core-data


    【解决方案1】:

    如果您使用CoreStore 进行数据库处理,一切都会变得更容易。您使用以下代码设置数据库:

    let dataStack: DataStack = {
        let dataStack = DataStack(xcodeModelName: "ModelName")
        do {
            try dataStack.addStorageAndWait()
        } catch let error {
            XCTFail("Cannot set up database storage: \(error)")
        }
        return dataStack
    }()
    

    假设你有以下数据库对象:

    class User: NSManagedObject {
    
        @NSManaged var name: String
    
        func rename(name: String, transaction: BaseDataTransaction?) {
            guard let user = transaction.edit(self) else {
                return
            }
            user.name = name
        }
    
    }
    

    您可以像这样运行单元测试:

    class FileTests: XCTestCase {
    
        func testRename() {
            // 1. Arrange
            var user: User?
            do {
                try dataStack.perform(synchronous: { transaction in
                    user = transaction.create(Into<User>())
                    user!.name = "Test"
                })
            } catch let error {
                XCTFail("Cannot perform database transaction: \(error)")
            }
    
            // 2. Action
            do {
                try dataStack.perform(synchronous: { transaction in
                guard let user = transaction.edit(user!) else {
                    return
                }
                user.rename(name: "Renamed", transaction: transaction)
                })
            } catch let error {
                XCTFail("Cannot perform database transaction: \(error)")
            }
    
            // 3. Assert
            do {
                try dataStack.perform(synchronous: { transaction in
                guard let user = transaction.edit(user!) else {
                    return
                }
                XCTAssertEqual(user.name, "Renamed")
                })
            } catch let error {
                XCTFail("Cannot perform database transaction: \(error)")
            }
        }
    
    }
    

    这只是开始。您可以简化和改进整个过程,但我希望您能理解。

    【讨论】:

    • 非常感谢。有点学习曲线,但我让它在我的测试中工作,而无需运行整个应用程序。代码内模式规范是关键——它使您不必使用 .xcdatamodeld 文件,因此不会出现丑陋的 Bundle 问题。谢谢。
    猜你喜欢
    • 2016-01-02
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    相关资源
    最近更新 更多