Hello,
I'm trying to unit test a SwiftData Model, but I'm facing a weird situation and couldn't find an answer to the problem.
The first solution I'll show you does not work, the second one does.
First of all, here's my @Model :
@Model
class Movie {
var name: String
init(name: String) {
self.name = name
}
}
First solution (doesn't work) :
var modelContainer: ModelContainer {
do {
let container = try ModelContainer(for: Movie.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
return container
} catch {
fatalError("Failed to create container.")
}
}
@MainActor
final class Tests: XCTestCase {
private var context: ModelContext!
override func setUp() {
context = modelContainer.mainContext
}
func testCountShouldBeOne() {
let movie = Movie(name: "NewMovie")
context.insert(movie) // Thread 1: EXC_BREAKPOINT
do {
let descriptor = FetchDescriptor<Movie>()
let movies = try context.fetch(descriptor)
XCTAssertEqual(movies.count, 1)
} catch {
XCTFail()
}
}
}
Second solution (works) :
(modelContainer remains the same)
@MainActor
final class Tests: XCTestCase {
private var container: ModelContainer!
override func setUp() {
container = modelContainer
}
func testCountShouldBeOne() {
let movie = Movie(name: "NewMovie")
container.mainContext.insert(movie)
do {
let descriptor = FetchDescriptor<Movie>()
let movies = try container.mainContext.fetch(descriptor)
XCTAssertEqual(movies.count, 1)
} catch {
XCTFail()
}
}
}
So the difference is I now have to call container.mainContext for SwiftData operations, and it works but in the first solution, where I had : context.something, I got the EXC_BREAKPOINT.
Does somebody know why?
Thank you !
In both cases, modelContainer
is a computed property that always returns a new ModelContainer
when you access it.
In the first case, you just get the modelContext
out of the ModelContainer
, without holding onto the ModelContainer
. The ModelContainer
gets deallocated as a result and you are now left with a ModelContext
that doesn't have an associated container.
In the second case, you do store the ModelContainer
as a stored property of the Tests
class, so the test case is keeping it alive.
The exact syntax you use to access the model context isn't really relevant. The key point here is whether or not you are keeping the ModelContainer
returned by the computed property alive. If you feel container.mainContext
is too long, you can write a computed property like this in the Tests
class.
var modelContext: ModelContext { container.mainContext }