@grynspan Thank you for your reply.
My use case is SwiftData migration tests that need a file-backed SQLite store, living alongside a large E2E test suite that uses in-memory stores.
Migration tests must use file-backed storage because SwiftData migrations require closing and reopening the ModelContainer — you can't migrate an in-memory database since it's destroyed on close:
@Suite(.serialized) class MigrationTests {
var url: URL!
var container: ModelContainer!
var context: ModelContext!
init() {
url = FileManager.default.temporaryDirectory.appending(component: "default.store")
// clean up any leftover DB files
try? FileManager.default.removeItem(at: url)
}
@Test func migrateTo1_2_0() throws {
// 1. Create a container with the OLD schema, insert test data
container = try initModelContainer(for: OldSchema.self, with: nil, storeUrl: url)
context = ModelContext(container)
OldSchema.insertMocks(context: context)
// 2. Close and reopen with the NEW schema + migration plan
// (this is why in-memory doesn't work — the DB must survive the close)
container = try initModelContainer(for: NewSchema.self, with: MyMigrationPlan.self, storeUrl: url)
context = ModelContext(container)
let records = try context.fetch(FetchDescriptor<NewSchema.SaveData>())
#expect(records.count == 1)
}
}
These tests are .serialized because they share a file path and each test writes → closes → reopens the SQLite store.
E2E tests use in-memory stores and are fully independent — each nested suite creates its own ModelContainer, so they can safely run concurrently:
@Suite struct E2ETests {
@Suite struct Moves {
let session = GameSession(store: inMemoryModelContainer())
@Test func magnetMove() async throws { /* ... */ }
@Test func positionMove() async throws { /* ... */ }
}
@Suite struct Physics {
let session = GameSession(store: inMemoryModelContainer())
@Test func gravity() async throws { /* ... */ }
}
// ~12 more nested suites, all independent
}
The conflict: if MigrationTests and E2ETests run at the same time, the file-backed migration tests can interfere with the global SwiftData state (schema registration, container caching). Today I work around this with .disabled on MigrationTests and run them separately, but what I actually want is:
@Suite(.serialized) struct AllTests {
@Suite(.serialized) struct MigrationTests { /* file-backed, must be serial */ }
@Suite(.concurrent) // ← this doesn't exist
struct E2ETests { /* in-memory, safe to parallelize */ }
}
The root .serialized would ensure migrations finish before E2E starts (or vice versa), while .concurrent on E2E would let its ~200 tests run in parallel as they do today. Without this, my options are either disable migration tests (current workaround) or serialize everything (which makes the E2E suite painfully slow).