Unit testing your iOS app with CoreData (Swift)

Kate Dmitrieva
2 min readApr 8, 2022

In order to test an iOS application that uses CoreData for data storage you need two things: context number one and context number two. In case if you try and save all your test items into the main app CoreData context, you will end up with data collision and not only your tests will fail, but also the app will receive test data mixed up with real data and that will be very confusing.

One of the possible approaches to include multiple CoreData contexts in your project is passing it as a variable of NSManagedObjectContext type into a CoreDataManager class (or whatever you name it). This class can be used by your model (or viewController) and also by your XCTest, assuming each one initialises it with the corresponding context.

First, create an extension that will return a context only for unit-testing purposes. This store will be inMemory type — optimal for quick and short-lasting test functions (unlike SQLite store commonly used for normal app functions):

extension NSManagedObjectContext {// creating inMemory storage for testing CoreData methodsclass func contextForTests() -> NSManagedObjectContext {    let model = NSManagedObjectModel.mergedModel(from: Bundle.allBundles)!    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)    try! coordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)    let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)    context.persistentStoreCoordinator = coordinator    return context}}

Create a proper context in your XCTest class and pass it to the test case:

override func setUp() {    self.context = NSManagedObjectContext.contextForTests()}...func testCreateNewItem() {    let manager = CoreDataManager(context: context!)    manager.createItem(itemText: "TEST ITEM")    let items = manager.fetchAllItems()    XCTAssertTrue(items.count == 1)    XCTAssertTrue(items.first?.todoText == "TEST ITEM")}

At the same time, on your app side the CoreDataManager must be initialised with another context, this is how it can look if you are creating a CoreData stack in your AppDelegate:

let manager = CoreDataManager(context: (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext)

From now on even though your tests and your app are calling the same CoreData methods, the test data will always be separated from the “real” data and your app data management will remain clear and well-organised.

--

--