我的单元测试核心数据设置存在问题。
我在AppDelegate中使用默认/新的Core Data堆栈设置
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
}()
}
为了测试,我有一个自定义函数来创建托管上下文
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
}
我有一个扩展名来查找给定谓词的第一个项目。
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
}
}
这是问题所在。
此测试通过:
func testExample() {
let context = setupInMemoryManagedObjectContext()
let entity = try! Entity.first(with: nil, in: context)
XCTAssertEqual(entity, nil)
}
但是如果在didFinishLaunchingWithOptions中触发了persistentContainer加载
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
}
然后我收到以下错误
失败:捕获&#34; NSInvalidArgumentException&#34;, &#34; executeFetchRequest:error:不是有效的NSFetchRequest。&#34;
错误特别来自_first()
中的这一行let fetchRequest = self.fetchRequest() // fetchRequest is <uninitialized> in that case
但如果我先修改测试以创建实体,那么测试再次运行正常(XCTAssertEqual当然不同......):
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,Swift 3.1,部署目标是iOS 10.3。
答案 0 :(得分:3)
看起来这是单元测试目标中的核心数据问题(也许在使用泛型时也是如此)。我也在工作中遇到过它,这是我们的解决方案:
Handle<Map> map = Handle<Map>::cast(args[0]);
答案 1 :(得分:2)
我不熟悉班级功能fetchRequest
,并且在查找此问题之前并不知道它存在。
我looked up some references但必须承认我仍然不明白发生了什么。
最近我一直在使用Xcode创建的自动生成的类,每个实体包含一个fetchRequest
方法,如下所示:
@nonobjc public class func fetchRequest() -> NSFetchRequest<User> {
return NSFetchRequest<Entity>(entityName: "Entity")
}
将其用作格式我将_first
函数中的一行更改为:
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
}
对我来说,这可以防止错误 - 我发布此信息是为了帮助您,但我还需要进行更多调查,以了解为什么这样做。< / p>