Meziantou.Framework.SnapshotTesting
3.1.8
Prefix Reserved
dotnet add package Meziantou.Framework.SnapshotTesting --version 3.1.8
NuGet\Install-Package Meziantou.Framework.SnapshotTesting -Version 3.1.8
<PackageReference Include="Meziantou.Framework.SnapshotTesting" Version="3.1.8" />
<PackageVersion Include="Meziantou.Framework.SnapshotTesting" Version="3.1.8" />
<PackageReference Include="Meziantou.Framework.SnapshotTesting" />
paket add Meziantou.Framework.SnapshotTesting --version 3.1.8
#r "nuget: Meziantou.Framework.SnapshotTesting, 3.1.8"
#:package Meziantou.Framework.SnapshotTesting@3.1.8
#addin nuget:?package=Meziantou.Framework.SnapshotTesting&version=3.1.8
#tool nuget:?package=Meziantou.Framework.SnapshotTesting&version=3.1.8
Meziantou.Framework.SnapshotTesting
Meziantou.Framework.SnapshotTesting validates serialized values against snapshot files stored on disk.
Basic usage
public sealed class SampleTests
{
[Fact]
public void ValidateUser()
{
var value = new { Name = "John", Age = 42 };
Snapshot.Validate(value);
}
}
For typed snapshots:
Snapshot.Validate(imageBytes, SnapshotType.Png);
Snapshot.Validate(svgText, SnapshotType.Svg);
For GIF/ICO frame snapshots (opt-in, emitted as PNG snapshots):
var settings = SnapshotSettings.Default with { };
settings.Serializers.AddGifSerializer();
settings.Serializers.AddIcoSerializer();
Snapshot.Validate(gifBytes, SnapshotType.Gif, settings);
Snapshot.Validate(icoBytes, SnapshotType.Ico, settings);
File naming convention
Snapshots are stored in a __snapshots__ directory next to the test source file:
- expected snapshots:
*.verified.<extension> - mismatch output:
*.actual.<extension>
Example:
__snapshots__/SampleTests_ValidateUser.verified.txt__snapshots__/SampleTests_ValidateUser.actual.txt
Notes:
- By default, snapshot names include class name and test name to avoid collisions across test classes.
.actualfiles are always written when a snapshot does not match.- If a single assertion serializes multiple files, an index suffix (
_0,_1, ...) is appended. - If names are too long, already end with
.verified/.actual, or contain characters that are not valid in a file name, a stable hash is added. Removing those characters would let two test names -Case_a/bandCase_a?b, say - claim the same snapshot file, and the hash keeps them apart. The hash does not depend on where the repository is checked out.
Storing snapshots in git
Snapshots are often binary: PNG frames from the GIF/ICO serializers, whatever the ImageSharp and
SkiaSharp backends emit, and .bin for any value whose extension is unknown. Add an entry to your
.gitattributes so git never applies line-ending conversion to them:
**/__snapshots__/** -text
Without it, a repository using the default core.autocrlf=true on Windows rewrites the line endings
of every binary snapshot on checkout. The symptom is snapshots that pass for whoever approved them
and fail for everyone else. PNG files fail to decode outright rather than mis-comparing, because the
PNG signature contains a CR LF pair specifically to catch this, but the reported error
("Unsupported image format") does not point at the cause.
Snapshot naming
You can choose how snapshot names are generated using SnapshotSettings.SnapshotNamingStrategy:
SnapshotNamingStrategies.TestNameSnapshotNamingStrategies.ClassName_TestName(default)SnapshotNamingStrategies.FullName
Calling Snapshot.Validate from a helper method
Wrapping Snapshot.Validate in a helper method is supported. The snapshot is still named after the test,
not after the helper: the test method is read from the test framework context (Xunit v3, TUnit, and NUnit)
and, under a framework that exposes no context (Xunit v2, MSTest), by walking the stack until a method
carrying a test attribute ([Fact], [Theory], [Test], [TestMethod]) is found.
The snapshot directory, however, comes from [CallerFilePath], which points at the file declaring the
helper. Forward the caller information so the snapshots are created next to the test file:
public static class ApiSnapshot
{
public static void ValidateOpenApiSpec(
string spec,
[CallerFilePath] string? filePath = null,
[CallerLineNumber] int lineNumber = -1)
{
var settings = SnapshotSettings.Default with { /* shared configuration */ };
Snapshot.Validate(spec, "yaml", settings, filePath, lineNumber);
}
}
public sealed class OpenApiTests
{
[Fact]
public void ValidateSpec()
{
ApiSnapshot.ValidateOpenApiSpec(GetSpec());
// => __snapshots__/OpenApiTests_ValidateSpec.verified.yaml
}
}
No custom SnapshotNamingStrategy or SnapshotPathStrategy is needed for this scenario.
This also works when the helper is async and awaits before asserting, as long as the test framework exposes
a context. Under a test framework that exposes no context (Xunit v2, MSTest), the test method must still be on
the call stack, so await inside the helper after the call to Snapshot.Validate rather than before it.
If the same test calls the helper several times, set Snapshot.TestContext to give each call a distinct name
(see Test context).
Snapshots stored as source files
Some snapshots are source files (for example the output of a source generator, see Meziantou.Framework.SnapshotTesting.Roslyn).
The package ships MSBuild targets that remove **/__snapshots__/**/*.cs and **/__snapshots__/**/*.vb from the Compile items and add them as None items, so they still show up in the IDE but are not compiled with the test project.
Set SnapshotTestingExcludeSnapshotFilesFromCompilation to false to opt out:
<PropertyGroup>
<SnapshotTestingExcludeSnapshotFilesFromCompilation>false</SnapshotTestingExcludeSnapshotFilesFromCompilation>
</PropertyGroup>
Approving snapshots
To approve generated *.actual.* files, you can use the dedicated tool package:
dotnet tool install --global Meziantou.Framework.SnapshotTesting.Tool
Meziantou.Framework.SnapshotTesting.Tool approve
Use --interactive to approve or reject snapshots one by one.
Snapshot types
SnapshotType controls extension and optional metadata (MimeType, DisplayName). This can also affect the serializer.
Test context
Snapshot naming uses test context when available:
Snapshot.TestContext(AsyncLocal<SnapshotTestContext?>) can be set explicitly.- Xunit v3, TUnit, and NUnit display names are auto-detected to improve generated file names.
- The test class and method names are auto-detected from the same frameworks and are used as-is. The call
stack is only walked for a name the context does not provide: under Xunit v2, MSTest, or no test framework,
or when
Snapshot.TestContextis set to a context that carries noClassNameorMethodName. - A test declared in a base class is named after the class it ran in, as reported by the test framework.
Customization
Use SnapshotSettings to customize behavior:
Serializers(SnapshotSerializerCollection)Comparers(SnapshotComparerCollection)SnapshotUpdateStrategy(Disallow,Overwrite,OverwriteWithoutFailure,MergeTool,MergeToolSync)SnapshotPathStrategyfor full path generation
A custom SnapshotNamingStrategy or SnapshotPathStrategy receives a SnapshotPathContext. Its ClassName
and MethodName come from the test framework context when it exposes them, and from the call stack otherwise.
The walk is the most expensive part of an assertion and only runs when a strategy reads a name the context did
not provide: a strategy that uses neither name never pays for it. Use MemberName when the name of the method
that called the assertion - captured by the compiler, always available - is enough.
You can also set the default strategy using the SNAPSHOTTESTING_STRATEGY environment variable.
The value is case-insensitive and must match one of the SnapshotUpdateStrategy static property names (for example: DISALLOW, MergeTool, overwritewithoutfailure).
var settings = SnapshotSettings.Default with
{
SnapshotUpdateStrategy = SnapshotUpdateStrategy.Disallow,
};
Snapshot.Validate(value, SnapshotType.Default, settings);
The default serializers handle human-readable objects, byte[], and Stream.
GIF frame extraction is opt-in via Serializers.AddGifSerializer(): when enabled and SnapshotType.Gif is used with a valid GIF byte[], each frame is serialized as a separate .png snapshot.
ICO image extraction is opt-in via Serializers.AddIcoSerializer(): when enabled and SnapshotType.Ico is used with a valid ICO byte[], each icon image is serialized as a separate .png snapshot.
BMP/PNG/JPEG/TIFF image comparison is opt-in via Comparers.AddImageComparer(). When enabled, SnapshotType.Bmp, SnapshotType.Png, SnapshotType.Jpeg (including .jpg aliases), and SnapshotType.Tiff (including .tif aliases) snapshots are compared by decoded pixel content (ARGB), so format metadata differences do not trigger snapshot mismatches.
To allow small visual differences, configure the image comparer with an SSIM threshold or a maximum 64-bit dHash/pHash Hamming distance. When multiple thresholds are configured, all comparisons must pass. dHash and pHash comparisons allow images with different dimensions, while exact and SSIM comparisons require identical dimensions.
var settings = SnapshotSettings.Default with { };
settings.Comparers.AddImageComparer(new ImageComparisonSettings
{
SimilarityThreshold = 0.95f,
DHashThreshold = 5,
PHashThreshold = 5,
});
Scrubbing
Scrubbing helps make snapshots deterministic by removing unstable values or lines.
var settings = SnapshotSettings.Default with { };
settings.ConfigureHumanReadableSerializer(options => options.ScrubGuid());
settings.ScrubLinesContaining("GeneratedAt:");
Snapshot.Validate(value, SnapshotType.Default, settings);
You can also scrub relative temporal values:
var now = DateTime.UtcNow;
var settings = SnapshotSettings.Default with { };
settings.ConfigureHumanReadableSerializer(options => options.UseRelativeDateTime(now));
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
-
net10.0
- Meziantou.Framework.DiffEngine (>= 2.0.8)
- Meziantou.Framework.FullPath (>= 3.0.8)
- Meziantou.Framework.HumanReadableSerializer (>= 4.0.5)
- Meziantou.Framework.LLMContext (>= 2.0.1)
- System.IO.Hashing (>= 10.0.12)
-
net11.0
- Meziantou.Framework.DiffEngine (>= 2.0.8)
- Meziantou.Framework.FullPath (>= 3.0.8)
- Meziantou.Framework.HumanReadableSerializer (>= 4.0.5)
- Meziantou.Framework.LLMContext (>= 2.0.1)
- System.IO.Hashing (>= 10.0.12)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Meziantou.Framework.SnapshotTesting:
| Package | Downloads |
|---|---|
|
Meziantou.Framework.SnapshotTesting.ImageSharp
Enables verification of SixLabors.ImageSharp images using file-based snapshots |
|
|
Meziantou.Framework.SnapshotTesting.SkiaSharp
Enables verification of SkiaSharp images using file-based snapshots |
|
|
Meziantou.Framework.SnapshotTesting.Roslyn
Enables verification of Roslyn objects (source generator results, syntax trees, and diagnostics) using file-based snapshots |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.1.8 | 42 | 9/13/2026 |
| 3.1.7 | 47 | 9/11/2026 |
| 3.1.6 | 82 | 9/9/2026 |
| 3.1.5 | 131 | 9/6/2026 |
| 3.1.4 | 120 | 9/3/2026 |
| 3.1.3 | 122 | 9/3/2026 |
| 3.1.2 | 241 | 8/30/2026 |
| 3.1.1 | 121 | 8/29/2026 |
| 3.1.0 | 134 | 8/24/2026 |
| 3.0.3 | 117 | 8/23/2026 |
| 3.0.2 | 127 | 8/16/2026 |
| 3.0.1 | 275 | 7/8/2026 |
| 3.0.0 | 230 | 7/5/2026 |
| 2.1.12 | 158 | 7/5/2026 |
| 2.1.11 | 129 | 7/3/2026 |
| 2.1.10 | 149 | 7/2/2026 |
| 2.1.9 | 143 | 6/28/2026 |
| 2.1.8 | 140 | 6/22/2026 |
| 2.1.7 | 153 | 6/14/2026 |
| 2.1.6 | 139 | 6/13/2026 |