Snapshot Testing in .NET with Scand Storm Petrel
Snapshot testing is a technique in modern software development that guarantees quality of the code output in primary use cases and their variations. By capturing the expected results as snapshots and comparing them against actual results during test execution, developers can quickly confirm intended changes or detect unintended modifications.
Traditionally, snapshot testing has been widely used for UI testing, where screenshots of rendered pages are compared. Still, the concept extends far beyond images and is useful for verifying HTML structures, JSON or XML responses, PDF reports, and other kinds of output.
In the .NET ecosystem, using snapshot testing requires a good tool that can automate snapshot generation, updating, and maintenance. This is where Scand Storm Petrel comes in. The Scand Storm Petrel NuGet packages (GitHub repository) provide a structured and automated approach to managing expected baselines in unit and integration tests.
What Is Snapshot Testing?
The term “Snapshot Testing” originates from capturing an actual screen snapshot and comparing it with an expected snapshot. However, the term is not limited to images and extends to other types of actual code execution results, such as HTML, JSON, XML, PDF, etc. Typically, Snapshot Unit/Integration Testing involves the following steps in a test method:
- Arrange: Create the expected snapshot.
- Act: Capture the actual snapshot.
- Assert: Compare the expected and actual snapshots for equality.
As we know, the Scand Storm Petrel NuGet packages automate the generation/rewriting of expected baselines with actual values. Let’s explore what they can do in the context of snapshot values.
Snapshot Testing with Scand.StormPetrel.Generator
The Scand.StormPetrel.Generator NuGet package implements automated generation/rewriting of any type of expected values kept in C# code. Thus, we can conclude:
- HTML, JSON, XML, and other text snapshots can be tracked as C# code values, as seen in the test methods of the SnapshotTest class according to the documented use cases.
- Binary snapshots can also be tracked as C# code values, as demonstrated in the same SnapshotTest example.
Therefore, Snapshot Unit/Integration Testing steps become regular steps of unit/integration testing, even without including Scand.StormPetrel.Generator:
- Arrange: Create the expected snapshot value as a variable, test attribute parameter, or a value in a test data source method body.
- Act: Capture the actual snapshot by executing the C# code being tested.
- Assert: Compare the expected and actual snapshots for equality using any assertion library/methods the developer prefers.
Snapshot Testing with Scand.StormPetrel.Generator without Serialization
In many cases, .NET developers can avoid serializing actual values to JSON, XML, or other formats and instantiate the expected snapshot directly in C# code, which corresponds to the primary use case of Scand.StormPetrel.Generator:
In the context of Snapshot Testing, a developer can utilize benefits such as:
- Easier detection and review of specific property expected values tracked in the tests via the “Find All References” command in Visual Studio or similar commands in other IDEs.
- Easier renaming of specific properties in both regular code and test code.
- Easier assertion of actual/expected values with assertion library methods like FluentAssertions’ BeEquivalentTo, e.g., when the order of properties or array elements in the expected baseline does not matter, or some properties can be ignored.
- Faster test execution in typical cases.
Snapshot Testing with Scand.StormPetrel.FileSnapshotInfrastructure
In some cases, it makes sense to keep snapshots as individual files in the file system. A developer can utilize benefits such as:
- Easier review and comparison of expected snapshots via specialized software for images, JSON, XML, PDF, or other viewers/comparers.
- Easier integration of .NET tests with other technologies. A real example is when expected JSON snapshots for a .NET RESTful API Service become input data for JavaScript/TypeScript unit tests of a client application for the service.
- Faster test execution in some cases, such as when a performance test needs to assert large files.
The expected snapshot files can also be generated/rewritten with Scand StormPetrel. You need to utilize the Scand.StormPetrel.FileSnapshotInfrastructure in this case according to its documentation. Snapshot Unit/Integration Testing steps are very similar to regular steps of unit/integration testing and become:
- Arrange: Read the expected snapshot value from a file. You need to call the Scand.StormPetrel.FileSnapshotInfrastructure.SnapshotProvider.ReadAllText method or use other alternatives described in the use cases.
- Act: Capture the actual snapshot by executing the C# code being tested.
- Assert: Compare the expected and actual snapshots for equality using any assertion library/methods the developer prefers.
Best Practices for Snapshot Testing in .NET
Snapshot testing is a great way to guarantee quality of the code output. But to keep it useful and manageable, developers should follow some practices—also when using Scand Storm Petrel.
First, snapshots should always be stored in version control, just like regular source code. This makes it easier to watch amendments, revert to previous versions if needed, and collaborate with a team. If snapshots are stored in separate files, only stable and necessary ones should be visible in repository history to avoid unnecessary clutter in the repository.
Even though Scand Storm Petrel can automatically update snapshots, changes should always be reviewed before accepting them. When a test fails due to a snapshot mismatch, the developer should check if the difference is expected or if it reveals a bug. Using Git diffs or specialized comparison tools for JSON, XML, CSV, DOCX, and other formats can help with this review process.
It’s also important to keep snapshots small and readable. Large, complex snapshots are harder to maintain and debug. Instead of storing entire objects, developers should only include the properties necessary for validation. When working with structured data, assertion libraries like FluentAssertions provide flexible comparison methods, such as ignoring the order of properties or excluding unnecessary fields.
For larger snapshots, such as JSON API responses, reports, or binary files like images, storing snapshots in external files is a better option than embedding them in code.
Scand.StormPetrel.FileSnapshotInfrastructure helps manage file-based snapshots, which makes it easier to review and update them when necessary. This approach is also useful when working with frontend applications that depend on API responses stored as JSON snapshots.
To get the most out of snapshot testing, it should be part of the Continuous Integration (CI) pipeline. Running snapshot tests in CI/CD workflows helps catch unexpected changes and prevents regressions. CI tools such as GitHub Actions, Azure DevOps, or GitLab CI can be configured to trigger snapshot tests when code is pushed, proving that only intended alterations are merged.
While snapshot testing is powerful, it should not be used for everything. It works best for structured outputs, such as API responses, UI components, and configuration files but is not ideal for simple logic, calculations, or highly variable data (such as timestamps). If snapshots contain dynamic data, those fields should be excluded or standardized before comparison to prevent false test failures.
For better test reliability, snapshot testing can be combined with assertion libraries that provide more control over comparisons. Instead of strict equality checks, FluentAssertions allows for flexible assertions, such as ignoring specific properties or directing cases where property order doesn’t matter.
For example, if a snapshot includes timestamps, excluding that field proves tests will fail only for meaningful changes.
Finally, snapshot testing should complement, not replace, other testing methods. Snapshot testing can be considered in the context of unit tests, integration tests, and end-to-end tests for full test coverage alongside traditional testing. A mix of snapshot and traditional tests can confirm better software quality.
Real-World Use Cases of Snapshot Testing Tools in .NET
One of the most common uses of snapshot testing is in API response validation. Web services, such as RESTful or GraphQL APIs, should return consistent data structures to establish proper communication between the backend and frontend.
For example, an online store’s product catalog API should always return product details in the same format. If a developer accidentally renames a field or alters the data format, snapshot testing will flag the difference. Instead of manually checking API responses after every change, developers can rely on snapshot tests to instantly spot unwanted modifications.
Snapshot testing is also valuable for frontend validation, especially in Blazor and Razor-based applications. UI components need to render correctly, and even small layout or structure changes can affect the user experience. By capturing the expected HTML output of a component and comparing it with new versions, developers can quickly catch problems.
For instance, a team working on a Blazor dashboard may update a component without realizing it slightly shifts the layout. A snapshot test would highlight this difference, helping the team fix the flaw before it reaches users.
For applications that generate reports or documents, snapshot testing can have a big difference too. Many businesses rely on PDF, Excel, or CSV reports that must maintain a specific format and structure. A financial system, for example, might generate monthly invoices for customers.
If an update unintentionally misaligns a table or changes the layout, snapshot testing will detect it before the reports are sent out.
One more use case is serialization testing, which confirms data is correctly converted between formats. Many .NET applications work with JSON or XML to exchange data, and any change in serialization logic can cause issues.
Take a logistics company that tracks shipments in JSON format. If an update changes how certain fields are stored—perhaps “deliveryDate” serialized field is reconfigured to “delivery_date”, or “DeliveryDate”, or even renamed to “shipmentDate”—it could break integrations with partner systems. Snapshot testing helps catch these defects by comparing serialized outputs to previous versions, allowing developers to confirm whether a change is expected or a mistake.
Finally, in microservices architectures, snapshot testing plays a great role in maintaining compatibility between different services. When one microservice depends on another, any unexpected change in response formats can cause failures.
A fintech company, for instance, may update its payment processing service, but a minor change in the API response structure could break the billing system. Snapshot tests prevent such issues by proving that microservices return expected outputs unless a change is intentional.
Conclusions
Scand Storm Petrel utilizes NuGet and .NET Incremental Generators infrastructure and can be actively used in Snapshot Testing. With minimal or no side effects on unit/integration snapshot test structure and behavior, it efficiently allows the generation/rewriting of expected snapshots, speeding up and simplifying software development. For more information, please contact us.