2014-09-12 16 views
7

Tôi có một ứng dụng Swift sử dụng NSFetchedResultsController để lấy List đối tượng từ cửa hàng khăng khăng:Làm thế nào để đơn vị kiểm tra NSFetchedResultsController trong Swift

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

và nó hoạt động như mong đợi, tôi nhận được List đối tượng giới thiệu in ra đến giao diện điều khiển. Tôi muốn viết một số bài kiểm tra đơn vị cho ứng dụng của mình, vì vậy tôi đã tạo lớp mở rộng XCTestCase. Mã biên dịch mà không có vấn đề gì, kiểm tra chạy, nhưng tiếc là tôi không thể tìm nạp các đối tượng List trong ngữ cảnh đó.

Tất cả tôi nhận được trong giao diện điều khiển là đếm List đối tượng và một lỗi nghiêm trọng:

lists count = 59 
fatal error: NSArray element failed to match the Swift Array Element type 

vươn bởi dòng:

for list: List in lists { 

Tôi khá chắc chắn tôi có mục tiêu cấu hình đúng, vì tôi có thể tạo đối tượng List và chèn nó vào ngữ cảnh đối tượng được quản lý mà không gặp sự cố nào từ mã nguồn của ứng dụng của tôi cũng như từ mã nguồn thử nghiệm đơn vị. Vấn đề duy nhất tôi gặp phải là tìm nạp từ đơn vị kiểm tra. Tôi tự hỏi tại sao tìm nạp đang hoạt động khi chạy ứng dụng trong trình mô phỏng và không thành công khi được thực thi trong khi kiểm tra đơn vị.

Bất kỳ ý tưởng nào có thể sai sẽ được đánh giá cao.

Cập nhật:

Để cụ thể hơn cách thực hiện của tôi trông như thế nào, đây là mẫu mã hoàn chỉnh mà tôi đang chơi với:

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

Khi tôi thực hiện nó từ mã nguồn ứng dụng của tôi, chạy ứng dụng trong trình mô phỏng, đầu ra giao diện điều khiển trông giống như sau:

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"; 
}) 

Tuy nhiên, khi tôi thực thi mã này từ kiểm tra đơn vị, tôi nhận được kết quả này:

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 

Tôi hy vọng nó sẽ dễ hiểu hơn về vấn đề ở đâu. Bằng cách nào đó, tuyên bố này:

let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List] 

sản xuất một loạt các đối tượng List khi ứng dụng được chạy trong mô phỏng, nhưng nó vẫn thất bại sản nil khi được thực thi từ kiểm tra đơn vị.

+0

Bạn có tệp mô hình đối tượng ('momd') được bao gồm trong gói thử nghiệm của mình không? Và là thiết lập tệp mô hình để sử dụng phân lớp 'Danh sách' của bạn cho thực thể danh sách? – MrAlek

+0

@MrAlek - Tôi không chắc liệu tệp '.momd' của tôi có được bao gồm trong gói thử nghiệm hay không. Tôi đang tạo mô hình như thế: 'NSManagedObjectModel (NSBundle.mainBundle(). URLForResource (" CheckLists ", withExtension:" momd ")!)' Và nó làm việc cho mục tiêu chính cũng như mục tiêu thử nghiệm. Bạn có thể đặc sắc hơn không? – Darrarski

+0

@MrAlek - Trong tệp '.xcdatamodeld' của tôi, tôi đã thiết lập đúng lớp cho thực thể' List'. Tôi thậm chí có thể tạo ra thực thể mới trong các bài kiểm tra đơn vị của tôi và chèn nó vào ngữ cảnh. Nó tồn tại trong cơ sở dữ liệu khi ngữ cảnh được lưu. – Darrarski

Trả lời

2

Sự cố được kết nối với cấu hình mục tiêu. Tôi đã giải quyết được vấn đề với một giải pháp nhỏ.

Trước đây, để thực hiện List lớp thực thể có thể truy cập trong mục tiêu thử nghiệm đơn vị của tôi, tôi đã thêm nó vào mục tiêu này. Vì vậy, lớp List là hai mục tiêu. Trên thực tế, có hai lớp học List được Swift biết đến, một lớp cho mỗi mục tiêu: MyAppTarget.ListMyUnitTestTarget.List. Bộ điều khiển kết quả được trả về trả về mảng của các đối tượng MyAppTarget.List, nhưng trong mục tiêu thử nghiệm đơn vị, List được giả định là lớp MyUnitTestTarget.List.Đó là cách dòng mã này:

let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List] 

sản xuất nil khi được thực hiện từ mục tiêu kiểm tra đơn vị và không phải là mảng thích hợp khi được thực hiện từ mục tiêu chính. Để khắc phục điều đó tôi vừa thay đổi nó thành:

let fetchedLists: [MyAppTarget.List]? = fetchedResultsController.fetchedObjects as? [MyAppTarget.List] 

và làm cho công khai lớp List. Sau sự thay đổi đó, nó hoạt động như mong đợi.

Nhưng vẫn có một chút khó hiểu với tôi rằng MyAppTarget.List không thể được truyền tới MyUnitTestTarget.List. Hơn nữa, nó có nghĩa là tôi cần phải công khai mọi thực thể NSManagedObject lớp con để sử dụng nó bên trong các bài kiểm tra đơn vị. Cho đến nay tôi đã không tìm thấy giải pháp tốt hơn.

Có thể có cách tốt hơn để giải quyết vấn đề đó. Tôi không thấy tùy chọn để cho biết NSFetchedResultsController rằng tùy chọn này sẽ trả về MyAppTarget.List trong mục tiêu chính và MyUnitTestTarget.List trong mục tiêu thử nghiệm đơn vị. Nó sẽ luôn sử dụng cấu hình từ tệp .xcdatamodeld cho thực thể đã cho. Ngoài ra, ngay cả khi có cách để đúc MyAppTarget.List vào MyUnitTestTarget.List bên trong một thử nghiệm đơn vị, nó vẫn sẽ yêu cầu lớp List được công khai.

Cập nhật:

Tôi đã tìm thấy một cách để thay đổi lớp của các đối tượng được trả về bởi NSFetchedResultsController trong thời gian chạy. Đó là một giải pháp rõ ràng và đơn giản hơn: https://stackoverflow.com/a/25858758/514181

Nó cho phép sử dụng liền mạch các thực thể CoreData trong các bài kiểm tra đơn vị, không cần truyền hoặc tạo lớp thực thể công khai.

+0

Bạn có ví dụ ở bất kỳ đâu không? Tôi đã thử phương pháp này nhưng đã trở lại lỗi nói rằng lớp học đã không được tìm thấy vì vậy nó đã được sử dụng NSManagedObject thay vì –

+1

Tôi có kèm theo ví dụ mã nguồn ở đây: http://stackoverflow.com/a/25858758/514181 – Darrarski

+1

Tôi đã cố gắng nhưng không vui sướng. Tôi có một ví dụ đơn giản tại https://github.com/ztolley/Frazzle –

Các vấn đề liên quan