Testing
How to write tests for Swap.Htmx applications.
Overview
Swap.Htmx applications are standard ASP.NET Core MVC apps, so you can use familiar testing tools:
- xUnit / NUnit / MSTest — Test frameworks
- Moq / NSubstitute — Mocking
- Microsoft.AspNetCore.Mvc.Testing — Integration tests
Additionally, the Swap.Testing package provides helpers specifically for testing Swap responses.
Unit Testing Controllers
Since SwapController methods return IActionResult, you can test them like any MVC controller.
public class TodosControllerTests
{
[Fact]
public void Index_ReturnsViewResult()
{
// Arrange
var controller = new TodosController();
// Act
var result = controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.NotNull(viewResult.Model);
}
}
Testing SwapResponse
When testing actions that return SwapResponse, you can inspect the response builder:
[Fact]
public void Add_ReturnsSwapResponseWithToast()
{
// Arrange
var controller = new TodosController();
// Act
var result = controller.Add("New Task");
// Assert
var swapResult = Assert.IsType<SwapActionResult>(result);
// Check that toast was added, targets were set, etc.
}
Integration Testing
Use WebApplicationFactory for full request/response testing.
public class TodosIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public TodosIntegrationTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Index_ReturnsSuccessAndCorrectContentType()
{
// Act
var response = await _client.GetAsync("/todos");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType?.ToString());
}
[Fact]
public async Task Index_WithHxRequest_ReturnsPartial()
{
// Arrange - Simulate HTMX request
var request = new HttpRequestMessage(HttpMethod.Get, "/todos");
request.Headers.Add("HX-Request", "true");
// Act
var response = await _client.SendAsync(request);
// Assert
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
// Partial should NOT contain <html> or <body> tags
Assert.DoesNotContain("<html", content);
Assert.DoesNotContain("<body", content);
}
}
Testing Event Handlers
SwapHandler classes can be tested in isolation by invoking HandleAsync directly.
public class StatsHandlerTests
{
[Fact]
public async Task HandleAsync_UpdatesStatsElement()
{
// Arrange
var handler = new StatsHandler(Mock.Of<IStatsService>());
var builder = new SwapResponseBuilder();
var evt = new TaskCompletedEvent(1);
// Act
await handler.HandleAsync(evt, builder, CancellationToken.None);
// Assert
// Verify builder contains expected AlsoUpdate call
// (Implementation depends on SwapResponseBuilder inspection API)
}
}
Best Practices
Test behavior, not implementation — Focus on what the user sees (correct HTML returned), not internal method calls.
Use integration tests for HTMX flows — Since Swap.Htmx relies on HTTP headers (
HX-Request), integration tests catch issues that unit tests miss.Mock external services — Inject mock repositories/services into controllers to isolate logic.
Test both browser and HTMX paths — Remember that
SwapViewbehaves differently based on request type. Test both.