---
title: "Start tests from fixtures and snapshots"
url: https://recursiveai.org/onegame/cookbook/fixtures-and-snapshots/
description: "Give tests a ready-made world with named fixtures, save a running game as a snapshot, and start a scenario from either, instead of playing up to the…"
version: "1.2.0"
---

# Start tests from fixtures and snapshots

Give tests a ready-made world with named fixtures, save a running game as a snapshot, and start a scenario from either, instead of playing up to the interesting moment every time.

## Fixtures: worlds built in code

A fixture is a named function that fills a new world. Add one to the apple tree from [Getting started](https://recursiveai.org/onegame/docs/getting-started/), with a sim method on `Tree` to build it:

```csharp
    // Sim code a fixture calls to build a world; not a command, so no player can send it.
    public void Ripen(int apples) => Apples = apples;
```

And in `OrchardGame`:

```csharp
    // Ready-made worlds that scenarios, tests and live control load by name.
    public override void DefineFixtures(FixtureSet fixtures) =>
        fixtures
            .Add("heavy-tree", world => world.Add(new Tree()).Ripen(40))
            .Add("bare-tree", world => world.Add(new Tree()));
```

A scenario starts from it with `fixture:`:

```yaml
name: heavy-tree
description: Start from a tree with 40 apples and pick ten.
fixture: heavy-tree
steps:
  - command: { entity: Tree, name: Pick, args: [10] }
  - step: 1
expect:
  - state: { entity: Tree, property: Apples, equals: 30 }
  - ui: { id: apples, text: "30 on the tree, 10 picked" }
```

```text
PASS heavy-tree (32 ms, tick 1, hash 78e6a5eeffa093c0)
1 passed, 0 failed
```

`fixture:` also works as a step, to switch worlds mid-scenario, and live control loads one with `fixture_load`.

## Snapshots: a saved game

A snapshot is the whole world at one tick: every entity, the random streams and the tick, with its state hash. Save one from a running game over live control. This session uses the small MCP client from [Drive a running game over MCP](https://recursiveai.org/onegame/cookbook/live-control-mcp/), which prints each tool call and its result:

```text
fixture_load {"name": "heavy-tree"} -> {"frame":0,"tick":0,"hash":"5a1f6ada5d6e7e3e"}
command_send {"entity": "Tree", "command": "Pick", "args": ["5"]} -> {"entity":"Tree#1","command":"Pick","status":"Queued"}
step {"frames": 120} -> {"frame":120,"tick":120,"hash":"8984cf483a6e339b"}
state_query {"entity": "Tree"} -> {"id":1,"type":"Tree","Apples":43,"Picked":5,"Growth":0}
snapshot_save {"path": "snapshots/after-pick.ogs"} -> {"path":"/tmp/onegame/orchard/artifacts/snapshots/after-pick.ogs","bytes":103,"hash":"8984cf483a6e339b","tick":120}
reset {} -> {"frame":120,"tick":0,"hash":"6554bfe7b94da5ca"}
state_query {"entity": "Tree"} -> {"id":1,"type":"Tree","Apples":0,"Picked":0,"Growth":0}
snapshot_load {"path": "snapshots/after-pick.ogs"} -> {"frame":120,"tick":120,"hash":"8984cf483a6e339b"}
state_query {"entity": "Tree"} -> {"id":1,"type":"Tree","Apples":43,"Picked":5,"Growth":0}
quit {} -> {"frame":120,"tick":120,"hash":"8984cf483a6e339b"}
```

The loaded world has the hash it was saved with. File paths are relative to the game’s output folder (`artifacts/` unless the game was started with `--artifacts`), and a path that leaves it is refused.

Copy the file next to the scenarios and start from it with `snapshot:`:

```yaml
name: from-snapshot
description: Carry on from a saved game with 43 apples and 5 picked, at tick 120.
snapshot: snapshots/after-pick.ogs
steps:
  - command: { entity: Tree, name: Pick, args: [3] }
  - step: 1
expect:
  - state: { entity: Tree, property: Picked, equals: 8 }
  - tick: 121
```

```text
PASS from-snapshot (29 ms, tick 121, hash 4ad2aa9eebde135b)
1 passed, 0 failed
```

The scenario carries on from tick 120, so after one step it is at tick 121.

## Good to know

- A snapshot loads only into a game with the same seed. After a `reset` with another seed, `snapshot_load` fails with `bad_data`: “The save could not be loaded: SeedMismatch (None).”
- In a single-player game a save is a snapshot: `GameInstance.SaveSnapshot()` returns the bytes and `LoadSnapshot(bytes)` restores them. Snapshots are versioned. A `[Migrate]` method on an entity loads state saved by an older schema.
- `Determinism.SnapshotRestore` proves a game survives a save and load mid-run: see [Test a game from C#](https://recursiveai.org/onegame/cookbook/csharp-game-tests/).
- Loading a fixture or a snapshot replaces the world, so a running recording starts a new segment there and replays still match.
