Core Data XCTestCases not working concurrently

I'm trying to create test cases for my Core Data driven view controllers. One problem I run into is managing temporary persistent containers when the test are running concurrently. I try to get around this by keeping a static copy of the NSManagedObjectModel to pass around the test cases, but it only kind of works. Here is the code -

class CoreDataTestCase: XCTestCase {
  static var model: NSManagedObjectModel?
  var mockPersistentContainer: NSPersistentContainer!
   
  override func setUp() {
    print("setUp")
    super.setUp()

    if let model = CoreDataTestCase.model {
      mockPersistentContainer = NSPersistentContainer(name: "MySchema", managedObjectModel: model)
    } else {
      mockPersistentContainer = NSPersistentContainer(name: "MySchema")
      CoreDataTestCase.model = mockPersistentContainer.managedObjectModel
    }
//    mockPersistentContainer.persistentStoreDescriptions[0].url = URL(fileURLWithPath: "/dev/null")
    let description = NSPersistentStoreDescription()
    description.type = NSInMemoryStoreType
    description.shouldAddStoreAsynchronously = false // Make it simpler in test env
    mockPersistentContainer.persistentStoreDescriptions = [description]
    mockPersistentContainer.loadPersistentStores { (_, error) in
      XCTAssertNil(error)
    }
...

When I run the tests individually they work, but when I run them concurrently I get the error

CoreData: warning: View context accessed for persistent container MySchema with no stores loaded CoreData: warning:   'MyEntity' (0x600002fa6260) from NSManagedObjectModel (0x600003bc8690) claims 'MySchema.MyEntity'. 2021-06-09 13:43:27.663078-0400 MySchema[76742:12179736] [error] error: +[MySchema.MyEntity entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass CoreData: error: +[MySchema.MyEntity entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass 2021-06-09 13:43:27.666042-0400 MySchema[76742:12179736] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObject of class 'MySchema.MyEntity' must have a valid NSEntityDescription.'

Replies

You have loaded multiple instances of your managed object model. What does CoreDataTestCase.model look like? If it's returning a new instance each time that's the source of your issue.

that's the static var static var model: NSManagedObjectModel? I check whether it's been created already and only initialize it if it hasn't     if let model = CoreDataTestCase.model {       mockPersistentContainer = NSPersistentContainer(name: "MySchema", managedObjectModel: model)     } else {       mockPersistentContainer = NSPersistentContainer(name: "MySchema")       CoreDataTestCase.model = mockPersistentContainer.managedObjectModel     }

  • I don’t think the simple if let is thread safe.

  • What would I need to do to make it thread safe?

Add a Comment

CoreDataTestCase.model is a single instance only created once per static scope

I don't believe there are any implicit concurrency guarantees there, and you don't have any locks or semaphores to prevent setUp from being called concurrently.

Enabling malloc stack logs will tell you where the other instance was initialized, I suspect setUp is being called concurrently (by multiple subclasses of your base test case, or perhaps xctest is parallelizing individual test methods?) if you have parallel testing enabled.

  • override class func setUp() {     let mockPersistentContainer = NSPersistentContainer(name: "MySchema")     CoreDataTestCase.model = mockPersistentContainer.managedObjectModel   }

    I tried moving the setup into the class setup function and got the same error, it seems that that should have fixed the thread issue?

Add a Comment