Skip to main content

freezeray generate

Generate FreezeRay artifacts (fixtures, schema tests, or migration tests) with granular control over the workflow.

Synopsis

freezeray generate <SUBCOMMAND> [OPTIONS]

# Short alias
freezeray g <SUBCOMMAND> [OPTIONS]

Description

The generate command provides granular operations for creating FreezeRay artifacts separately. Most important: Use generate migration-tests when starting work on a new schema version. This lets you write and test migrations as you develop. The other subcommands (fixtures, schema-tests) are rarely needed because freeze handles them automatically.

Subcommands

migration-tests - Generate migration tests ⭐

freezeray generate migration-tests --from-schema <VERSION> --to-schema <VERSION>
Generates a migration test file that validates data migration between two schema versions. This is the primary use case for the generate command. See detailed documentation →

fixtures - Generate frozen fixtures (Advanced)

freezeray generate fixtures --schema <VERSION>
Generates frozen SwiftData fixtures for a specific schema version. Rarely needed: freeze does this automatically. See detailed documentation →

schema-tests - Generate drift tests (Advanced)

freezeray generate schema-tests --schema <VERSION>
Generates a drift test file that validates a frozen schema hasn’t changed. Rarely needed: freeze does this automatically. See detailed documentation →

Why Use generate migration-tests?

The Correct Workflow

Migration tests should be created when you start working on a new schema version, not after:
# 1. Freeze V2
freezeray freeze 2.0.0

# 2. Generate migration test WHEN STARTING V3 work
freezeray generate migration-tests --from-schema 2.0.0 --to-schema 3.0.0

# 3. Edit the generated test, add custom assertions
# 4. Implement V3 schema + migration
# 5. Iterate: run test → fix migration → run test (⌘U)
# 6. When ready, freeze V3
freezeray freeze 3.0.0
This lets you develop with the migration test as you build the new schema.
Key insight: freeze does NOT generate migration tests. You must explicitly run generate migration-tests when starting work on a new schema version.

generate fixtures

Generate frozen fixtures for a specific schema version.

Synopsis

freezeray generate fixtures --schema <VERSION> [OPTIONS]

Description

Generates frozen SwiftData fixtures by:
  1. Discovering the @FreezeSchema(version: "X.Y.Z") annotation
  2. Running a freeze test in iOS Simulator
  3. Extracting fixtures from /tmp to FreezeRay/Fixtures/<VERSION>/

Arguments

--schema <VERSION>, -s <VERSION>

Required. The schema version to freeze (e.g., “1.0.0”). Must match a @FreezeSchema annotation in your source code:
@FreezeSchema(version: "3.0.0")
enum AppSchemaV3: VersionedSchema {
    // ...
}

Options

--simulator <NAME>

Default: iPhone 17 iOS Simulator to use for freezing:
freezeray generate fixtures --schema 3.0.0 --simulator "iPhone 15 Pro"

--scheme <NAME>

Default: Auto-detected Xcode scheme to build:
freezeray generate fixtures --schema 3.0.0 --scheme MyApp

--force

Overwrite existing fixtures (dangerous!):
freezeray generate fixtures --schema 3.0.0 --force
Only use --force during development before shipping to production. Overwriting frozen fixtures breaks immutability.

--output <PATH>

Default: FreezeRay/Fixtures Custom output directory:
freezeray generate fixtures --schema 3.0.0 --output /custom/path

--config <PATH>

Path to FreezeRay.yaml config file (experimental):
freezeray generate fixtures --schema 3.0.0 --config .freezeray.yml

What Gets Created

FreezeRay/Fixtures/3.0.0/
├── App-3.0.0.sqlite          # Frozen SQLite database
├── App-3.0.0.sqlite-shm      # SQLite shared memory file
├── schema-3.0.0.sql          # SQL DDL statements
├── schema-3.0.0.json         # Human-readable schema metadata
├── schema-3.0.0.sha256       # SHA256 checksum for drift detection
└── export_metadata.txt       # Timestamp and version info

Examples

Basic usage

freezeray generate fixtures --schema 3.0.0
Output:
🔍 Generating fixtures for schema version 3.0.0...
📱 Running freeze test in simulator (iPhone 17)...
📦 Extracting fixtures from simulator...
✅ Generated fixtures: FreezeRay/Fixtures/3.0.0/
   - App-3.0.0.sqlite
   - schema-3.0.0.sql
   - schema-3.0.0.json
   - schema-3.0.0.sha256
   - export_metadata.txt
   - App-3.0.0.sqlite-shm

Short form

freezeray g fixtures -s 3.0.0

Custom simulator

freezeray generate fixtures --schema 3.0.0 --simulator "iPhone 15"

Common Errors

”No @FreezeSchema found for version X.Y.Z”

Cause: The version doesn’t match any @FreezeSchema annotation. Fix: Add the annotation to your schema:
@FreezeSchema(version: "3.0.0")
enum AppSchemaV3: VersionedSchema {
    // ...
}

“Fixtures already exist”

Cause: Fixtures for this version already exist. Solutions: During development (before shipping):
freezeray generate fixtures --schema 3.0.0 --force
After shipping to production:
# Create new version instead
freezeray generate fixtures --schema 3.0.1

generate schema-tests

Generate drift test for a frozen schema version.

Synopsis

freezeray generate schema-tests --schema <VERSION> [OPTIONS]

Description

Generates a drift test file that validates the current schema definition matches the frozen fixtures. Requirements:
  • Fixtures must already exist for the specified version
  • Run freezeray generate fixtures first if needed

Arguments

--schema <VERSION>, -s <VERSION>

Required. The schema version to generate tests for (e.g., “3.0.0”).

Options

--force

Overwrite existing test file:
freezeray generate schema-tests --schema 3.0.0 --force
By default, existing test files are never overwritten (they’re user-owned).

--config <PATH>

Path to config file (experimental):
freezeray generate schema-tests --schema 3.0.0 --config .freezeray.yml

What Gets Created

// FreezeRay/Tests/AppSchemaV3_DriftTests.swift
import Testing
import FreezeRay
@testable import MyApp

/// Drift test for AppSchemaV3 v3.0.0
///
/// This test verifies that the current schema definition matches the frozen fixture.
/// If this test fails, it means the schema has been modified since it was frozen.
///
/// Note: Tests run serially to avoid SwiftData store conflicts
@Suite(.serialized)
struct AppSchemaV3_3_0_0_DriftTests {
    @Test("AppSchemaV3 v3.0.0 has not drifted")
    func testAppSchemaV3_3_0_0_Drift() throws {
        // Call the macro-generated check function
        try AppSchemaV3.__freezeray_check_3_0_0()

        // TODO: Add custom data validation here
        // Example:
        // - Verify specific model properties exist
        // - Check relationship configurations
        // - Validate index definitions
    }
}
Generated tests use @Suite(.serialized) to prevent parallel execution issues with SwiftData stores.

Examples

Basic usage

freezeray generate schema-tests --schema 3.0.0
Output:
🔍 Generating schema tests for version 3.0.0...
✅ Generated drift test: AppSchemaV3_DriftTests.swift

Short form

freezeray g schema-tests -s 3.0.0

Regenerate (overwrite)

freezeray generate schema-tests --schema 3.0.0 --force

Common Errors

”No fixtures found for version X.Y.Z”

Cause: Fixtures don’t exist for the specified version. Fix: Generate fixtures first:
freezeray generate fixtures --schema 3.0.0
freezeray generate schema-tests --schema 3.0.0

“Test file already exists”

Cause: The drift test file was already generated. Solutions: If you want to keep your custom assertions:
# Do nothing - the existing test is yours to maintain
If you want to regenerate:
freezeray generate schema-tests --schema 3.0.0 --force

generate migration-tests

Generate migration test from one schema version to another.

Synopsis

freezeray generate migration-tests --from-schema <VERSION> --to-schema <VERSION> [OPTIONS]

Description

Generates a migration test file that validates data migration between two schema versions. This is the key innovation of the generate command: you can create the migration test early in your development workflow, not after the fact. Requirements:
  • Fixtures must exist for --from-schema version
  • @FreezeSchema must exist in code for --to-schema version
  • SchemaMigrationPlan must have a migration stage between versions
Behavior if test file exists:
  • Does NOT overwrite (preserves user’s custom assertions)
  • Prints skip message
  • Use --force to overwrite

Arguments

--from-schema <VERSION>, -f <VERSION>

Required. Source schema version (e.g., “2.0.0”).

--to-schema <VERSION>, -t <VERSION>

Required. Target schema version (e.g., “3.0.0”).

Options

--force

Overwrite existing test file:
freezeray generate migration-tests -f 2.0.0 -t 3.0.0 --force
Only use --force if you’re sure you want to discard custom assertions you’ve added to the test.

--config <PATH>

Path to config file (experimental):
freezeray generate migration-tests -f 2.0.0 -t 3.0.0 --config .freezeray.yml

What Gets Created

// FreezeRay/Tests/MigrateV2_0_0toV3_0_0_Tests.swift
import Testing
import FreezeRay
@testable import MyApp

/// Migration test from v2.0.0 → v3.0.0
///
/// This test verifies that the migration path between these versions works correctly.
///
/// Note: Tests run serially to avoid SwiftData store conflicts
@Suite(.serialized)
struct MigrateV2_0_0toV3_0_0_Tests {
    @Test("Migrate v2.0.0 → v3.0.0")
    func testMigrateV2_0_0toV3_0_0() throws {
        // Test the migration using FreezeRayRuntime
        try FreezeRayRuntime.testMigration(
            from: AppSchemaV2.self,
            to: AppSchemaV3.self,
            migrationPlan: AppMigrations.self
        )

        // TODO: Add data integrity checks here
        // Example:
        // - Verify data is preserved during migration
        // - Check that new fields have default values
        // - Validate relationship updates
        // - Ensure no data loss for critical fields
    }
}
The generated test includes @Suite(.serialized) to fix parallel execution issues that cause SwiftData store conflicts.

Examples

Basic usage (early workflow)

# When starting V3 development
freezeray generate migration-tests --from-schema 2.0.0 --to-schema 3.0.0

# Edit generated test, add assertions
# Implement V3 schema + migration
# Iterate: run test → fix migration → run test
Output:
🔍 Generating migration test: v2.0.0 → v3.0.0...
✅ Generated migration test: MigrateV2_0_0toV3_0_0_Tests.swift
   Edit this file to add custom migration assertions

Short form

freezeray g migration-tests -f 2.0.0 -t 3.0.0

Regenerate (overwrite custom assertions)

freezeray generate migration-tests -f 2.0.0 -t 3.0.0 --force
Output:
✅ Generated migration test: MigrateV2_0_0toV3_0_0_Tests.swift

Common Errors

”No fixtures found for version X.Y.Z”

Cause: Source schema fixtures don’t exist. Fix: Freeze the source version first:
freezeray freeze 2.0.0
freezeray generate migration-tests -f 2.0.0 -t 3.0.0

“No @FreezeSchema found for version X.Y.Z”

Cause: Target schema doesn’t exist in code yet. Fix: Define the target schema:
@FreezeSchema(version: "3.0.0")
enum AppSchemaV3: VersionedSchema {
    // ...
}
Then generate the test:
freezeray generate migration-tests -f 2.0.0 -t 3.0.0

“No SchemaMigrationPlan found in codebase”

Cause: You haven’t defined a migration plan. Fix: Create a migration plan:
enum AppMigrations: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [AppSchemaV1.self, AppSchemaV2.self, AppSchemaV3.self]
    }

    static var stages: [MigrationStage] {
        [migrateV1toV2, migrateV2toV3]
    }

    static let migrateV2toV3 = MigrationStage.custom(
        fromVersion: AppSchemaV2.self,
        toVersion: AppSchemaV3.self,
        willMigrate: nil,
        didMigrate: { context in
            // Migration logic
        }
    )
}
Then generate the test:
freezeray generate migration-tests -f 2.0.0 -t 3.0.0

“Migration test already exists”

Cause: You’ve already generated the migration test. Solutions: If you’ve added custom assertions:
# Do nothing - keep your custom test
If you want to start over:
freezeray generate migration-tests -f 2.0.0 -t 3.0.0 --force

Complete Workflow Example

# 1. Ship V2
freezeray freeze 2.0.0

# 2. Start V3 development - generate migration test immediately
freezeray generate migration-tests -f 2.0.0 -t 3.0.0

# 3. Edit generated test, add custom assertions
vim FreezeRay/Tests/MigrateV2_0_0toV3_0_0_Tests.swift

# 4. Implement V3 schema + migration
# (edit schema files, add migration stage)

# 5. Iterate: run tests normally as you develop
# ⌘U in Xcode or:
xcodebuild test -scheme MyApp

# 6. Ship V3
freezeray freeze 3.0.0  # Just generates fixtures + drift test
Key point: You develop with the migration test from the start, not after the fact.

Parallel Execution Fix

All generated tests now use @Suite(.serialized) to prevent parallel execution issues.

Problem (Before)

Migration tests failed when run in parallel (Swift Testing default):
swift test
# ❌ All 3 migration tests fail due to parallel execution
Running individually worked:
swift test --filter MigrateV2toV3
# ✅ Passes
Cause: Tests create/destroy SwiftData stores in temp directories with potential collisions.

Solution (After)

Generated tests now use @Suite(.serialized):
@Suite(.serialized)  // ← Prevents parallel execution
struct MigrateV2_0_0toV3_0_0_Tests {
    @Test func testMigrateV2_0_0toV3_0_0() throws {
        // ...
    }
}
Now tests work in default harness:
swift test
# ✅ All tests pass

Best Practices

1. Generate Migration Tests Early

Generate the migration test when you start working on the migration:
# ✅ Good: Generate early
freezeray generate migration-tests -f 2.0.0 -t 3.0.0
# Edit test, add assertions
# Implement migration
# Iterate until test passes

# ❌ Bad: Generate after implementing
# (implement migration)
# (write custom test)
freezeray freeze 3.0.0  # Conflict!

2. Never Overwrite User Tests

The generate commands never overwrite existing test files by default:
# First time: creates file
freezeray generate migration-tests -f 2.0.0 -t 3.0.0
# ✅ Generated migration test

# Second time: skips
freezeray generate migration-tests -f 2.0.0 -t 3.0.0
# ⚠️  Migration test already exists (preserving custom assertions)
This protects your custom assertions.

3. Use freeze for Complete Workflow

For most users, freeze is still the recommended command:
# This is still the easiest workflow
freezeray freeze 1.0.0  # Does everything
Only use generate when you need fine-grained control:
  • Generate migration tests early ✅
  • Regenerate specific artifacts ✅
  • Customize the workflow ✅

4. Commit Generated Tests

Generated tests are yours to maintain:
freezeray generate migration-tests -f 2.0.0 -t 3.0.0

# Add custom assertions
vim FreezeRay/Tests/MigrateV2_0_0toV3_0_0_Tests.swift

# Commit with schema changes
git add FreezeRay/Tests/
git commit -m "Add migration test for V2→V3 with data integrity checks"
Test files evolve with your schema, just like any other test.

What freeze Does

The freeze command is simpler than generate - it only creates fixtures and drift tests:
freezeray freeze 3.0.0
Internally equivalent to:
# 1. Generate fixtures (via simulator)
freezeray generate fixtures --schema 3.0.0

# 2. Generate drift test (if doesn't exist)
freezeray generate schema-tests --schema 3.0.0
That’s it! freeze does NOT touch migration tests. Migration tests are managed separately with generate migration-tests.

Requirements

Same as freezeray freeze:
  • macOS 14+
  • Xcode 15+
  • iOS Simulator (iPhone 17 recommended)
  • Project structure:
    • @FreezeSchema(version: "X.X.X") annotations
    • Test target for running freeze tests
    • Xcode project or Swift Package with iOS target

Next Steps