使用简单的init在测试中创建CoreData对象

时间:2019-04-04 12:56:01

标签: ios swift xcode testing core-data

我有一组NSManagedObject使用的ClassToBeTested子类。

ClassToBeTested仅在NSManagedObject子类的几个属性上运行,不需要关系或整个CoreData堆栈。

我可以通过常规方式以相同方式在测试中使用相同的对象吗?

        let template = CoreDataClass()
        template.name = randomString(length: 40) // This fails!
        templates.append(template)

当前它因错误而失败:

  

失败:捕获到“ NSInvalidArgumentException”,“-[CoreDataClass   setTemplate_name:]:无法识别的选择器已发送到实例   0x600000af4c40“

1 个答案:

答案 0 :(得分:1)

尽管我尝试执行此操作时遇到了另一个错误(未调用指定的初始化程序),但是在两种情况下,您的问题的答案都是:不,您不能这样做。

但是,如今有了NSPersistentContainer,使用单例内存Core Data Stack进行此类测试很容易。在测试包中包含数据模型,然后将其放入测试的全局范围:

var sharedTestContext: NSManagedObjectContext = {
    // Swift is smart enough to execute this only once.
    let container = NSPersistentContainer(name: "<YourDataModelName>")
    let description = NSPersistentStoreDescription()
    description.type = NSInMemoryStoreType
    container.persistentStoreDescriptions = [description]
    container.loadPersistentStores { (description, error) in
        if let error = error {
            fatalError("Failed to load store for test: \(error)")
        }
    }
    return container.newBackgroundContext()
}()

并定义一个特殊的托管对象初始化程序进行如下测试:

/**
 Initializes a managed object for testing

 - important:  This assumes your managed object subclass name is the same
 as its entity name.
 */
public extension NSManagedObject {
    convenience init(testContext: NSManagedObjectContext?) {
        let context = testContext ?? sharedTestContext
        /*  The following contraption is necessary to avoid warning:
         "Multiple NSEntityDescriptions claim the NSManagedObject subclass"
         For explanation see:
         https://stackoverflow.com/questions/51851485/multiple-nsentitydescriptions-claim-nsmanagedobject-subclass */
        let name = String(describing: type(of: self))
        let entity = NSEntityDescription.entity(forEntityName: name, in: context)!
        self.init(entity: entity, insertInto: context)
    }
}

现在您可以这样创建测试对象:

let template = CoreDataClass(testContext: nil)