Create and review a first snapshot
Install the built-in engine in the test project:
dotnet add package XBullet.EasyTesting.Snapshots.Core
Import XBullet.EasyTesting.Snapshots. Snapshot an arbitrary serializable value with
SnapshotAssert.MatchAsync, raw JSON with MatchJsonAsync, or an HTTP controller response with
ShouldMatchControllerSnapshot.
Snapshot a controller response
This executable example calls an authenticated controller and records its request method, relative URL, numeric status, reason phrase, stable headers, and body:
[Fact]
public Task Authenticated_controller_matches_own_snapshot() =>
Run(async (scope, cancellationToken) =>
{
using var client = scope.CreateAuthenticatedClient(
TestUser.Create(name: "Grace", nameIdentifier: "user-84"));
using var response = await client.GetAsync("/api/secure/me", cancellationToken);
await response.ShouldMatchControllerSnapshot(
snapshotSettings: BuiltInSnapshotAudit.CreateSettings(),
cancellationToken: cancellationToken);
});
JSON bodies are structural. Text remains text, empty content becomes null, and binary content is
stored as base64. Volatile and credential-bearing headers are excluded by default.
Review the first failure
With no verified file, the assertion writes a runtime-qualified received file and throws
SnapshotMismatchException. Review it, promote it, and run the test again:
[Fact]
public async Task New_snapshot_writes_a_received_file()
{
var cancellationToken = TestContext.Current.CancellationToken;
var snapshotDirectory = CreateTemporarySnapshotDirectory();
var settings = new SnapshotSettings()
.InDirectory(snapshotDirectory)
.Named("new-snapshot")
.Updating(SnapshotUpdateMode.None)
.WithoutDiffTool();
try
{
var exception = await Assert.ThrowsAsync<SnapshotMismatchException>(() =>
SnapshotAssert.MatchAsync(new { Value = 42 }, settings, cancellationToken));
Assert.True(File.Exists(exception.ReceivedPath));
Assert.False(File.Exists(exception.VerifiedPath));
Assert.Matches(@"\.received\.net(8|9|10)\.0\.json$", exception.ReceivedPath);
var verifiedPath = SnapshotAssert.AcceptReceived(exception.ReceivedPath);
Assert.Equal(exception.VerifiedPath, verifiedPath);
Assert.True(File.Exists(verifiedPath));
await SnapshotAssert.MatchAsync(new { Value = 42 }, settings, cancellationToken);
}
finally
{
DeleteTemporarySnapshotDirectory(snapshotDirectory);
}
}
For an ordinary test, the files look like:
__snapshots__/
OrderTests.Get_order.received.net10.0.json
OrderTests.Get_order.verified.json
Commit the *.verified.* file. Do not commit *.received.* files. The verified file is shared by
all target frameworks; the received name includes the target framework so concurrent .NET 8, 9,
and 10 runs cannot overwrite one another.
Safe review checklist
Before accepting:
- Confirm every changed value represents intended behavior.
- Remove, ignore, hash, or redact nondeterministic and sensitive fields.
- Ensure the snapshot is small enough to review as a contract.
- Rerun the test with updates disabled.
- Commit the verified file together with the behavior change.
Use explicit assertions for a few scalar rules. Prefer a snapshot when the value is cohesive and a reviewer benefits from seeing the complete shape.
Next: file locations and naming, snapshot recipes, and stabilizing data.
See the snapshot core API reference.