我有一个Swift应用程序使用NSFetchedResultsController
从持久存储中获取List
个对象:
let fetchedResultsController: NSFetchedResultsController = ...
var error : NSError?
fetchedResultsController.performFetch(&error)
if let error = error {
NSLog("Error: \(error)")
}
let lists: [List] = fetchedResultsController.fetchedObjects! as [List]
NSLog("lists count = \(lists.count)")
for list: List in lists {
NSLog("List: \(list.description)")
}
并且它按预期工作,我将List
对象描述打印到控制台。
我想为我的应用程序编写一些单元测试,所以我创建了扩展XCTestCase
的类。代码编译没有问题,测试运行,但遗憾的是我无法在该上下文中获取List
个对象。
我进入控制台的所有内容都是List
个对象和一个致命错误:
lists count = 59
fatal error: NSArray element failed to match the Swift Array Element type
升起了这条线:
for list: List in lists {
我很确定我已正确配置目标,因为我可以创建List
对象并将其插入到托管对象上下文中,而不会出现我的应用程序源代码以及单元测试源代码的问题。我遇到的唯一问题是从测试单元获取。我想知道为什么在模拟器中运行应用程序时提取工作正常,并且在单元测试期间执行时失败。
任何可能出错的想法都将受到赞赏。
更新
更具体地说,我的实现方式如下,这里是我正在玩的完整代码示例:
var error: NSError? = nil
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let applicationDocumentsDirectory = urls[urls.count-1] as NSURL
let modelURL = NSBundle.mainBundle().URLForResource("CheckLists", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
let url = applicationDocumentsDirectory.URLByAppendingPathComponent("CheckLists.sqlite")
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
NSLog("Error1: \(error)")
abort()
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("List", inManagedObjectContext: managedObjectContext)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "name", ascending: true) ]
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: managedObjectContext,
sectionNameKeyPath: nil,
cacheName: "ListFetchedResultsControllerCache"
)
fetchedResultsController.performFetch(&error)
if let error = error {
NSLog("Error2: \(error)")
abort()
}
let fetchedObjects: [AnyObject]? = fetchedResultsController.fetchedObjects
if let fetchedObjects = fetchedObjects {
NSLog("Fetched objects count: \(fetchedObjects.count)")
for fetchedObject in fetchedObjects {
NSLog("Fetched object: \(fetchedObject.description)")
}
}
else {
NSLog("Fetched objects array is nil")
}
let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List]
if let fetchedLists = fetchedLists {
NSLog("Fetched lists count: \(fetchedLists.count)")
for fetchedList in fetchedLists {
NSLog("Fetched list: \(fetchedList.description)")
}
}
else {
NSLog("Fetched lists array is nil")
}
当我从应用程序的源代码执行它,在模拟器中运行应用程序时,控制台输出如下所示:
Fetched objects count: 3
Fetched object: <CheckLists.List: 0x7a6866f0> (entity: List; id: 0x7a686020 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: {
name = "List 1";
})
Fetched object: <CheckLists.List: 0x7a686930> (entity: List; id: 0x7a686030 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: {
name = "List 2";
})
Fetched object: <CheckLists.List: 0x7a686970> (entity: List; id: 0x7a686040 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: {
name = "List 3";
})
Fetched lists count: 3
Fetched list: <CheckLists.List: 0x7a6866f0> (entity: List; id: 0x7a686020 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: {
name = "List 1";
})
Fetched list: <CheckLists.List: 0x7a686930> (entity: List; id: 0x7a686030 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: {
name = "List 2";
})
Fetched list: <CheckLists.List: 0x7a686970> (entity: List; id: 0x7a686040 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: {
name = "List 3";
})
但是,当我从单元测试中执行此代码时,我得到了这个输出:
Fetched objects count: 3
Fetched object: <CheckLists.List: 0x7a07df50> (entity: List; id: 0x7a07d7e0 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: {
name = "List 1";
})
Fetched object: <CheckLists.List: 0x7a07e190> (entity: List; id: 0x7a07d7f0 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: {
name = "List 2";
})
Fetched object: <CheckLists.List: 0x7a07e1d0> (entity: List; id: 0x7a07d800 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: {
name = "List 3";
})
Fetched lists array is nil
我希望更容易理解问题所在。不知怎的,这句话:
let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List]
当app在模拟器中运行时,会生成一个List
个对象数组,但是当从单元测试执行时,它会生成nil
。
答案 0 :(得分:2)
该问题与目标配置有关。我通过一些解决方法解决了这个问题。
以前,为了在我的单元测试目标中访问List
实体类,我已将其添加到此目标中。所以,List
类有两个目标。实际上,Swift已知有两个List
类,每个目标一个:MyAppTarget.List
和MyUnitTestTarget.List
。重新绑定的结果控制器返回MyAppTarget.List
个对象的数组,但在单元测试目标中,List
被假定为MyUnitTestTarget.List
类。这就是这行代码:
let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List]
从单元测试目标执行时生成nil
,而不是从主目标执行时生成的正确数组。为了解决这个问题,我将其改为:
let fetchedLists: [MyAppTarget.List]? = fetchedResultsController.fetchedObjects as? [MyAppTarget.List]
并公开List
课程。在那次改变之后,它就像预期的那样工作
但是,MyAppTarget.List
无法投放到MyUnitTestTarget.List
对我来说有点困惑。而且,这意味着我需要公开每个实体NSManagedObject
子类,以便在单元测试中使用它。到目前为止,我找不到更好的解决方案。
也许有更好的方法来解决这个问题。我没有看到一个选项告诉NSFetchedResultsController
它应该在主目标中返回MyAppTarget.List
,在单位测试目标中返回MyUnitTestTarget.List
。它将始终使用.xcdatamodeld
文件中给定实体的配置。此外,即使有一种方法可以在单元测试中将MyAppTarget.List
强制转换为MyUnitTestTarget.List
,仍然需要将List
类公开。
<强>更新强>
我找到了一种在运行时更改NSFetchedResultsController
返回的实体类的方法。这是一个更清晰简单的解决方案:https://stackoverflow.com/a/25858758/514181
它允许在单元测试中无缝使用CoreData实体,而无需强制转换或使实体类公开。