A well-tested codebase is the foundation of confident software delivery. Yet anyone who has written tests for complex scenarios knows the pain: any number of Assert.Equal calls, each one a liability waiting to break when a property name changes or when you add a new field. Finding better ways to assert our software's behavior is an ongoing pursuit for all of us, and that pursuit led us to this quarter's Duende Open Source Sponsorship recipient: Verify.
In our fifth sponsorship, the team at Duende has chosen Verify as the next open-source recipient of our commitment to supporting projects that empower individuals, teams, communities, and organizations.
Now let's see what Verify is all about.
What is Verify?
Verify is a snapshot testing library for .NET, created and maintained by Simon Cropp. Rather than writing individual assertions, Verify serializes the result of an operation into a human-readable file, a "snapshot", and stores it alongside your tests. On every subsequent run, Verify compares the new output against the approved snapshot. If they match, the test passes. If they differ, the test fails, and a diff reveals exactly what changed, letting you decide whether the change is intentional or a bug.
This approach, also called approval testing, shines when you're asserting against complex objects, API responses, serialized documents, or any data structure where hand-writing dozens of assertions would be tedious and brittle. With over 3,400 stars on GitHub, support for six test frameworks (xUnit, NUnit, MSTest, TUnit, Fixie, and Expecto), and 73+ extension packages under the VerifyTests organization (including Verify.Http, Verify.EntityFramework, Verify.ImageSharp, and many more), Verify has grown into one of the most versatile testing tools in the .NET ecosystem. The project has been actively maintained since 2019 by Simon Cropp and reflects a genuine philosophy: assertions should describe intent, not enumerate every field of every object.
Getting Started
To add Verify to your project, install the package for your chosen test framework. Duende's products use xUnit v3, so we'll use Verify.XunitV3 here, but packages are also available for NUnit (Verify.NUnit), MSTest (Verify.MSTest), and TUnit (Verify.TUnit).
dotnet add package Verify.XunitV3
To see the value Verify brings, consider a test asserting against a user profile object. The traditional approach requires a separate assertion for every field:
[Fact]
public void Traditional_assertions_are_verbose()
{
var result = GetUserProfile();
Assert.Equal("Khalid", result.FirstName);
Assert.Equal("Abuhakmeh", result.LastName);
Assert.Equal("khalid@duendesoftware.com", result.Email);
Assert.True(result.IsActive);
Assert.Equal(3, result.Roles.Count);
Assert.Contains("Admin", result.Roles);
Assert.Contains("Developer", result.Roles);
Assert.Contains("Blogger", result.Roles);
}
This testing strategy gets unwieldy fast, and when the object's shape changes, every assertion may need to be updated. With Verify, the same test becomes a single line:
[Fact]
public Task Verify_simplifies_testing()
{
var result = GetUserProfile();
return Verify(result);
}
On the first run, Verify serializes the result into a .received.txt file and fails the test, prompting you to review the output. Once you approve it, the file is renamed to .verified.txt, and becomes the baseline. The file is committed to source control alongside your tests, and future runs automatically compare against it.
The verified file is human-readable and easy to review in a pull request:
{
FirstName: Khalid,
LastName: Abuhakmeh,
Email: khalid@duendesoftware.com,
IsActive: true,
Roles: [
Admin,
Developer,
Blogger
]
}
When the object shape changes, say, a new Department property is added, Verify catches it immediately. Instead of hunting down which assertion to add, you review the diff, approve it if intentional, and move on. This workflow scales beautifully as your models grow.
Verify Support for JetBrains Rider
One of Verify's biggest workflow improvements comes from an ecosystem contribution by Matthias Koch (@matkoch): the Verify Support plugin for JetBrains Rider (and ReSharper). The plugin brings the approval workflow directly into the IDE, eliminating the need to manage .received. and .verified. files manually through the file system.
With the plugin installed, the test runner's context menu gains a handful of powerful actions:
- Accept Received: promotes a
.received.file to.verified.with a single click - Accept + Re-run: accepts the snapshot and immediately re-runs the test to confirm it passes
- Compare Received vs. Verified: opens a visual side-by-side diff right inside the IDE
- Multi-selection support: batch accept or compare multiple snapshot changes at once

If you're a JetBrains Rider user, this plugin is a must-have companion to Verify. It turns what could be a context-switching chore into a seamless, IDE-native review experience. The plugin is maintained by Matthias as part of JetBrains' OSS Power-Ups initiative, ensuring it stays up to date with both Verify's releases and the latest Rider versions.
Verify at Duende
We don't just sponsor Verify; we use it. Across our products and foss repositories, Verify plays a critical role in protecting the public API surface of our libraries.
For security-focused products like Duende IdentityServer and Duende BFF, unintended breaking changes are especially costly. Even a subtle public API shift can break downstream consumers in ways that are hard to diagnose. To guard against this, we pair Verify with PublicApiGenerator to snapshot the entire exported API surface of each library. If a pull request accidentally adds, removes, or renames a public type or member, the test fails immediately, and the diff tells us exactly what changed.
Here's a simplified version of what those tests look like in our BFF repository:
public class PublicApiVerificationTests
{
[Fact]
public async Task VerifyPublicApi_Bff()
{
var apiGeneratorOptions = new ApiGeneratorOptions
{
IncludeAssemblyAttributes = false
};
var publicApi = typeof(BffBuilder).Assembly
.GeneratePublicApi(apiGeneratorOptions);
var settings = new VerifySettings();
_ = await Verify(publicApi, settings);
}
}
The resulting .verified.txt file is a full text representation of the assembly's public API, committed to the repository. We run similar tests across Duende.BFF, AccessTokenManagement, IdentityModel, and OAuth2Introspection. 13 snapshot files in total.
We also use Verify in our Duende IdentityServer SAML integration tests to snapshot XML metadata responses. Verify's scrubber feature handles dynamic values like host URLs, keeping the snapshots stable across environments:
var settings = new VerifySettings();
settings.AddScrubber(sb =>
{
sb.Replace(hostUri, "https://localhost");
});
await Verify(content, settings);
Check out our Livestream with Anders Abel of Sustainsys on the topic of SAML 2.0 and SAML support in Duende IdentityServer
Verify has become an essential part of how we maintain quality and API stability at Duende.
Sponsorship Details
To support the Verify project and Simon Cropp's outstanding work, Duende Software is sponsoring Verify for the next 12 months at $250 per month (totaling $3,000 for the year). This sponsorship will help the maintainer cover expenses and invest in the project's growth and promotion.
If you benefit from Verify, consider supporting the project as well. You can contribute in various ways, including financial contributions, code, documentation, or bug reports.
We'd like to thank Simon Cropp for his tireless work on Verify and the broader .NET testing ecosystem. If you haven't tried snapshot testing yet, give Verify a go! We think you'll wonder how you ever wrote tests without it. Supporting open source is at the heart of what we do at Duende Software, and we're proud to help Verify continue to grow and innovate.
Thanks for stopping by!
We hope this post helped you on your identity and security journey. If you need a hand with implementation, our docs are always open. For everything else, come hang out with the team and other developers on GitHub.
If you want to get early access to new features and products while collaborating with experts in security and identity standards, join us in our Duende Product Insiders program. And if you prefer your tech content in video form, our YouTube channel is the place to be. Don't forget to like and subscribe!
Questions? Comments? Just want to say hi? Leave a comment below and let's start a conversation.