I need to unit test (XCTest
) some of my methods that include reference to CoreData models.
The following line execute correctly :
var airport: AnyObject! = Airport.MR_createEntity()
(lldb) po airport <Airport: 0x7fcf54216940> (entity: Airport; id: 0x7fcf54216a20 <x-coredata:///Airport/t1D3D08DA-70F9-4DA0-9487-BD6047EE93692> ; data: {
open = nil;
shortName = nil;
visible = nil; })
whereas the following line triggers an EXC_BAD_ACCESS
:
var airport2: Airport = Airport.MR_createEntity() as! Airport
(lldb) po airport2
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x0).
The process has been returned to the state before expression evaluation.
No sign of this error with my principal target. The configuration is : model objects in both targets, class prefixed by @objc(MyModel)
, no namespace in class' models in my xcdatamodel
Any idea what's going on here ?
Right, so I have finally gotten to the bottom of this and its not pretty. There is actually a radar for this issue as it appears to be a bug with the Swift compiler not recognizing ManagedObject casting in test targets. So add your voice to the noise
Starting off with an entity defined as follows:
@objc(Member)
class Member: NSManagedObject {
@NSManaged var name: String
}
I wrote a simple test class in which I create a MO in 3 different ways:
The first two failed:
let context = NSManagedObjectContext.MR_defaultContext()
func testMagicalRecordCreation() {
let m = Member.MR_createInContext(context) as? Member
XCTAssertNotNil(m, "Failed to create object")//fails
}
func testEntityDescriptionClassCreation() {
let m2 = NSEntityDescription.insertNewObjectForEntityForName("Member", inManagedObjectContext: context) as? Member
XCTAssertNotNil(m2, "Failed to create object")//fails
}
And then a success
func testManualMOCreation() {
let ent = NSEntityDescription.entityForName("Member", inManagedObjectContext: context)!
let m3 = Member(entity: ent, insertIntoManagedObjectContext: context)
XCTAssertNotNil(m3, "Failed to create object")
}
This means that right now you have two options. Write your tests in Objective-C; or create a utility method to insert test objects into a context using the means I showed above.
Theres a nice post about this behaviour here
I ended up using an NSManagedObjectContext extension to be used explicitly in Swift Tests:
extension NSManagedObjectContext {
func insertTestEntity<T: NSManagedObject>(entity: T.Type) -> T {
let entityDescription = NSEntityDescription.entityForName(NSStringFromClass(T.self), inManagedObjectContext: self)!
return T(entity: entityDescription, insertIntoManagedObjectContext: self)
}
}
And it could be used like this:
func testConvenienceCreation() {
let m4 = context.insertTestEntity(Member)
XCTAssertNotNil(m4, "Failed to create object")
}
More reading about this kind of approach here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With