如何将Core Data用于主要应用程序和单元测试?

时间:2019-03-10 14:29:48

标签: ios swift unit-testing core-data

当我尝试将Core Data与NSInMemoryStoreType一起用于单元测试时,总是会出现此错误:

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
    }
}

从我读到的here来看,这似乎是我遇到的同样问题,我必须在每次测试后清理所有(我想)自己正在做的事情。

我不能正确清理吗?还有另一种方法吗?

1 个答案:

答案 0 :(得分:0)

我知道这个问题很老了,但是,我最近遇到了这个问题,在其他地方没有找到答案。

基于@JamesBedford 的回答,设置核心数据堆栈的一种方法是:

  1. 确保您的应用中只有一个CoreDataStack单个实例,涵盖应用和测试目标。不要在测试目标中创建新实例。在您的应用程序目标中,您可以按照 James 的建议使用单例。或者,如果您在 AppDelegate 中保持对 Core Data 堆栈的强引用并在启动时进行初始化,请在您的应用程序目标中提供一个方便的静态属性以从您的测试目标访问。类似于:
extension CoreDataStack
    static var shared: CoreDataStack {
        (UIApplication.shared.delegate as! AppDelegate).stack
    }
}
  1. 在 Xcode 中为您的测试方案添加一个环境变量。转到 Xcode > Edit Scheme > Test > Arguments > Environment Variables。添加一个新的名称-值对,例如:name = "persistent_store_type", value = "in_memory"。然后,在运行时,在 CoreDataStack 初始化程序中,您可以使用 ProcessInfo 检查此环境变量。
final class CoreDataStack {
    
    let storeType: StoreType
    
    init() {
        if ProcessInfo.processInfo.environment["persistent_store_type"] == "in_memory" {
            self.storeType = .inMemory
        } else {
            self.storeType = .sqlLite
        }
    }
    
}

从这里开始,您的测试目标现在将使用 .inMemory 持久存储类型,并且不会创建 SQLLite 存储。你甚至可以添加一个单元测试断言 :)