Naming snapshots
editA snapshot is named after the test that validates it: <Class>_<Test> by default. The test class and method come from the test framework when it exposes them (xUnit v3, NUnit and TUnit), and from the call stack otherwise (xUnit v2, MSTest). A test declared in a base class is named after the class it ran in.
Several assertions in one test
Each call to Snapshot.Validate in a test gets its own file. The first call keeps the name of the test, the next ones get an ordinal suffix (~2, ~3, ...):
var html = "<p>Hello</p>";
var png = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC");
Snapshot.Validate(html); // __snapshots__/SnapshotTests_ReadmeExample_SeveralAssertions.verified.txt
Snapshot.Validate(png, SnapshotType.Png); // __snapshots__/SnapshotTests_ReadmeExample_SeveralAssertions~2.verified.pngThe calls are numbered by the line they are made from, in the order they first run, so they must run in a deterministic order:
- Calls made from the same line, in a loop or through a helper that does not forward the caller information, share a name. Give each of them its own name with the test context.
- A call that does not run renumbers the calls after it. With
if (OperatingSystem.IsWindows()) Snapshot.Validate(png);followed bySnapshot.Validate(text);, the text snapshot isName~2.verified.txton Windows andName.verified.txtelsewhere. Give such calls their own name with the test context instead. - The numbers are kept for the lifetime of the test process, so that a test that runs again in the same process (a retried test, the cases of a parameterized NUnit fixture) gets the same names. In a long-lived host, such as continuous testing in a single process, a call added above an existing one gets the next number until the process restarts.
The suffix is only added by the built-in naming strategies: a custom strategy may already tell the calls apart.
Parameterized tests
The name of a parameterized test is the method name followed by its arguments: SampleTests_Render_1.5_True.
nullis written asnull. The string"null"is quoted, so its name gets a hash and differs from the name ofnull.- Arrays and lists are written element by element:
SampleTests_Render_1_2_<hash>fornew[] { 1, 2 }. DateTime,DateTimeOffsetandTimeOnlyuse the round-trip format (O), so values that only differ by a fraction of a second or by their kind get distinct names.- A custom display name (
DisplayName, TestName) is used as it is, including its.characters. - The arguments of a parameterized NUnit fixture (
[TestFixture("en-US")]) or of a TUnit test class ([Arguments]on the class) are appended to the name, so each instance of the class gets its own snapshot:FormatTests_Format_en-US. NUnit 3 does not expose them: its instances are reported as a name collision.
Name a snapshot yourself
Set Snapshot.TestContext to give a call a name of your choice. It is an AsyncLocal<SnapshotTestContext?>: the value flows to the calls made by the current test, and must be reset when you are done:
var documents = new Dictionary<string, string>
{
["json"] = """{ "name": "John" }""",
["yaml"] = "name: John",
};
try
{
foreach (var (format, content) in documents)
{
// Calls made from the same line share a name: give each one its own
Snapshot.TestContext.Value = new SnapshotTestContext(TestName: "Export_" + format);
Snapshot.Validate(content, format); // __snapshots__/SnapshotTests_Export_json.verified.json, ...
}
}
finally
{
Snapshot.TestContext.Value = null;
}SnapshotTestContext takes a TestName and optional Metadata. The test name replaces the name of the test method; the class name is still added by the default naming strategy. Metadata is not part of the file name: it is available to custom naming and path strategies. ClassName and MethodName can also be set with an object initializer.
Naming strategies
SnapshotSettings.SnapshotNamingStrategy builds the name from the test:
| Strategy | Name |
|---|---|
| SnapshotNamingStrategies.ClassName_TestName (default) | SampleTests_ValidateUser |
| SnapshotNamingStrategies.FullName | SampleTests.ValidateUser |
| SnapshotNamingStrategies.TestName | ValidateUser |
var settings = SnapshotSettings.Default with
{
SnapshotNamingStrategy = SnapshotNamingStrategies.FullName,
};
Snapshot.Validate("Hello", settings); // __snapshots__/SnapshotTests.DocumentationExample_NamingStrategy.verified.txtTestName drops the class name: two classes of the same folder with a test of the same name then get the same snapshot name, a name collision.
A custom strategy is a SnapshotNamingStrategy delegate that receives a SnapshotPathContext and returns the name. Its ClassName and MethodName come from the test framework when it exposes them, and from the call stack otherwise; the stack walk is the most expensive part of an assertion, and only runs when the strategy reads a name the framework did not provide. Use MemberName, the name of the method that called Snapshot.Validate as captured by the compiler, when it is enough.
Name collisions
Two tests that get the same snapshot name would overwrite each other's snapshot. When they run in the same process, the second one fails with a SnapshotException that names both tests. This happens, for instance, with:
- two tests that have the same display name;
- test cases whose arguments do not override
ToString, or only differ by type (1,1Land"1"passed to anobjectparameter); - test contexts that only differ by Metadata;
- names that only differ by case (
Case_AandCase_aare the same file on Windows and macOS).
Give one of the tests a distinct name, for example with Snapshot.TestContext. Tests that explicitly set the same SnapshotTestContext.TestName may share a snapshot. Collisions are only detected with the default path strategy and a built-in naming strategy.
An assertion that produces several files (Name_0, Name_1) cannot tell whether a Name_2 file is one it produced in an earlier run or the snapshot of a test named Name_2: such a file is reported as unexpected, but never deleted.
Names from earlier versions
Earlier versions truncated a display name at its last ., named arrays after their type, formatted dates without their fraction of a second and ignored the arguments of the test class. The snapshot files created with those names keep being used as long as they exist and no other test uses the name; delete them to switch to the new names.