Swap.Testing
1.3.0
dotnet add package Swap.Testing --version 1.3.0
NuGet\Install-Package Swap.Testing -Version 1.3.0
<PackageReference Include="Swap.Testing" Version="1.3.0" />
<PackageVersion Include="Swap.Testing" Version="1.3.0" />
<PackageReference Include="Swap.Testing" />
paket add Swap.Testing --version 1.3.0
#r "nuget: Swap.Testing, 1.3.0"
#:package Swap.Testing@1.3.0
#addin nuget:?package=Swap.Testing&version=1.3.0
#tool nuget:?package=Swap.Testing&version=1.3.0
Swap.Testing
Swap.Testing provides small helpers for writing integration tests against HTMX-powered ASP.NET Core MVC applications.
It is designed to work alongside Swap.Htmx but does not require it.
Install
dotnet add package Swap.Testing
Key types
HtmxTestFixture<TProgram>– wrapsWebApplicationFactory<TProgram>and gives you anHtmxTestClient<TProgram>.HtmxTestClient<TProgram>– fluent client with HTMX-aware helpers (HtmxGetAsync,HtmxPostAsync,SubmitFormAsync, …).HtmxTestResponse– wrapsHttpResponseMessageand exposes HTML and HTMX assertions (elements, attributes, HX headers, snapshots, validation, etc.).SnapshotManager– optional snapshot testing helper for HTML responses.
Basic usage
- Create a test project and reference your ASP.NET Core app project.
- Add Swap.Testing to the test project.
- Use
HtmxTestFixture<TProgram>as an xUnit fixture:
public class TodosTests : IClassFixture<HtmxTestFixture<Program>>
{
private readonly HtmxTestClient<Program> _client;
public TodosTests(HtmxTestFixture<Program> fixture)
{
_client = fixture.Client;
}
[Fact]
public async Task GetTodos_ReturnsPartialWithItems()
{
var response = await _client.HtmxGetAsync("/todos");
await response
.AssertSuccess()
.AssertPartialViewAsync()
.AssertElementCountAsync(".todo-item", expectedCount: 5);
}
[Fact]
public async Task UpdateCounter_RazorPage_ReturnsUpdatedCount()
{
// Use the Razor Pages helper to target a specific handler
var response = await _client.HtmxGetPageHandlerAsync("/Counter", "Increment", new { count = 5 });
await response
.AssertSuccess()
.AssertContainsAsync("Count: 6");
}
}
Form and redirect helpers
SubmitFormAsync– finds a<form>in a previous HTML response, readshx-*oraction/method, collects inputs, and submits as an HTMX request.FollowHxRedirectAsync– follows anHX-Redirectheader and returns the resulting HTMX response.
Snapshot testing
Use AssertMatchesSnapshotAsync to compare HTML output against a stored snapshot.
await response
.AssertSuccess()
.AssertMatchesSnapshotAsync("todo-list-partial");
The first run creates __snapshots__/todo-list-partial.html. Future runs compare normalized HTML. Set UPDATE_SNAPSHOTS=true to update snapshots.
More examples
See EXAMPLE_TESTS.cs in this folder for a longer example suite using most of the helpers.
Cookie Persistence
Cookies automatically persist across requests within the same HtmxTestClient instance, enabling session-based and authentication testing:
// Login sets a session cookie — persists automatically
await _client.HtmxPostAsync("/auth/login", loginData);
// Subsequent requests include the session cookie
var dashboard = await _client.GetAsync("/dashboard");
await dashboard.AssertSuccess().AssertContainsAsync("Welcome");
// Inspect cookies
var cookies = _client.Cookies.GetAllCookies();
// Clear for test isolation
_client.ClearCookies();
OOB Swap Introspection
Inspect and assert out-of-band swap elements in responses:
var response = await _client.HtmxPostAsync("/shop/purchase", data);
// Get all OOB swaps as structured data
var swaps = await response.GetOobSwapsAsync();
// Each OobSwap has: TargetId, SwapMode, HtmlContent
// Fluent assertions
await response
.AssertOobSwapExistsAsync("cart-count")
.AssertOobSwapContentAsync("cart-count", "5")
.AssertOobSwapCountAsync(3);
Trigger Payload Assertions
Deep-inspect HX-Trigger JSON payloads:
response
.AssertTriggerPayload("showToast", "message", "Created")
.AssertTriggerPayload("itemCreated", "data.id", "123")
.AssertTriggerCount(2);
// Typed deserialization
var toast = response.GetTriggerPayload<ToastData>("showToast");
Form Field Assertions
Assert form field existence and values:
await response
.AssertFormFieldExistsAsync("Email")
.AssertFormValueAsync("Name", "John")
.AssertFormValueAsync("Active", "true"); // checkbox
Snapshot Scrubbers
The SnapshotManager supports custom scrubbers for dynamic content:
SnapshotManager.ScrubUrls(); // Replace URLs → [URL]
SnapshotManager.ScrubRegex(@"tenant-\w+", "[TENANT]"); // Custom pattern
SnapshotManager.ClearScrubbers(); // Reset
Built-in default scrubbers automatically handle GUIDs → [GUID], ISO dates → [DATETIME], and anti-forgery tokens → [TOKEN].
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
-
net10.0
- AngleSharp (>= 1.1.2)
- Microsoft.AspNetCore.Mvc.Testing (>= 10.0.0)
-
net8.0
- AngleSharp (>= 1.1.2)
- Microsoft.AspNetCore.Mvc.Testing (>= 8.0.11)
-
net9.0
- AngleSharp (>= 1.1.2)
- Microsoft.AspNetCore.Mvc.Testing (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.