Testing Migrations
Learn how to write comprehensive migration tests with custom validation to ensure your SwiftData migrations preserve data integrity.Overview
FreezeRay helps you write comprehensive migration tests with custom validation to ensure your SwiftData migrations preserve data integrity. What you’ll learn:- How to generate migration tests early in your workflow (new!)
- How scaffolded migration tests work
- Adding custom data validation
- Testing edge cases (empty databases, large datasets)
- Common migration pitfalls
Two Workflows: Old vs. New
❌ Old Workflow (generate after)
✅ New Workflow (generate early)
Use
freezeray generate migration-tests to create the test when you start working on the migration, not after!Prerequisites
- You’ve frozen at least one schema version (e.g., 1.0.0)
- You have a
SchemaMigrationPlandefined (or plan to create one) - You’re ready to start working on the next schema version
The Scaffolded Migration Test
You can generate a migration test in two ways:- Early (recommended):
freezeray generate migration-tests -f 1.0.0 -t 2.0.0 - Automatically:
freezeray freeze 2.0.0(if 1.0.0 fixtures exist)
Generated tests now use
@Suite(.serialized) to prevent parallel execution issues with SwiftData stores. This means tests work in the default test harness without special configuration!What This Test Does
- Loads the frozen v1.0.0 SQLite database from
FreezeRay/Fixtures/1.0.0/App-1_0_0.sqlite - Copies it to a temporary location (bundle resources are read-only)
- Creates a ModelContainer with v2.0.0 schema and your migration plan
- Runs migration from v1.0.0 → v2.0.0
- Throws if migration crashes
Adding Custom Validation
Let’s add data validation to ensure migration preserves user data.Example Migration Scenario
Schema V1 (v1.0.0):Step 1: Seed Test Data in V1
Before freezing v1.0.0, add test data to your frozen database:You can seed data either manually (via Xcode’s data model editor) or programmatically before freezing.
Step 2: Add Validation to Migration Test
Update the scaffolded test with custom checks:What This Validation Checks
- Record count - No users were lost during migration
- Existing fields -
nameandemailare preserved - New fields -
createdAthas default values (not nil) - Data types - Fields have sensible values
Common Validation Patterns
Pattern 1: Verify Record Counts
Ensure no data is lost:Pattern 2: Check Required Fields
Ensure non-optional fields have values:Pattern 3: Validate New Fields
Check that new fields have default values or proper initialization:Pattern 4: Verify Relationships
Ensure relationships are intact after migration:Pattern 5: Check Data Transformations
If migration transforms data, verify correctness:Testing Custom Migrations
For custom migrations withdidMigrate logic, test your transformation code:
Custom Migration Example
Test Custom Logic
Testing Edge Cases
Empty Database
Test that migration works with no data:Large Dataset
Test performance with many records:Multi-Hop Migrations
Test migrations that span multiple versions: V1 → V2 → V3Multi-hop migrations test cumulative effects of multiple migrations. Users might skip app versions, so always test the full path.
Common Migration Issues
Issue 1: Data Loss in Custom Migrations
Symptom: Records disappear after migration Cause:Issue 2: Nil Values for Non-Optional Fields
Symptom: Optional fields become nil unexpectedly Cause: SwiftData can’t provide defaults for new non-optional fields Fix:Issue 3: Broken Relationships
Symptom: Relationships are nil after migration Cause: Inverse relationships not maintained Fix: Use SwiftData’s@Relationship properly with inverses
Test:
Best Practices
1. Test with Real Data
Use your frozen fixtures with realistic data:2. Document Expected Behavior
Add comments explaining what each validation checks:3. Test Edge Cases
Don’t just test happy paths:4. Run Tests in CI
Migration tests should run on every PR:Troubleshooting
”Fixture not found” error
Cause: Fixtures not included in test bundle Fix:- In Xcode, select your test target
- Build Phases → Copy Bundle Resources
- Add
FreezeRay/folder (ensure it’s a folder reference, not group)
Migration test passes locally, fails in CI
Cause: Fixtures not committed to git Fix:Test crashes with “cannot open database”
Cause: Trying to write to read-only bundle resource Fix: Always copy fixture to writable location:Summary
You’ve learned how to:- ✅ Understand scaffolded migration tests
- ✅ Add custom data validation
- ✅ Test edge cases (empty DB, large datasets)
- ✅ Verify multi-hop migrations
- ✅ Catch common migration issues
Next Steps
CI Integration
Run migration tests in your CI pipeline
Migration Testing Concepts
Deep dive into how migration testing works
First Freeze
Tutorial for freezing your first schema
Drift Detection
Understand drift detection mechanics