blazor-testing
Blazor Testing
Test Project Setup
dotnet new xunit -n MyApp.Tests
dotnet add MyApp.Tests package bunit
dotnet add MyApp.Tests package FluentAssertions
dotnet add MyApp.Tests package NSubstitute
dotnet add MyApp.Tests package Microsoft.AspNetCore.Mvc.Testing
dotnet add MyApp.Tests package Testcontainers.PostgreSql # For real DB tests
bUnit Component Tests
public sealed class CounterTests : TestContext
{
[Fact]
public void Counter_StartsAtZero()
{
var cut = RenderComponent<Counter>();
cut.Find("p").TextContent.Should().Contain("0");
}
[Fact]
public void Counter_IncrementsOnClick()
{
var cut = RenderComponent<Counter>();
cut.Find("button").Click();
cut.Find("p").TextContent.Should().Contain("1");
}
}
Testing with Parameters and Services
public sealed class ProductListTests : TestContext
{
[Fact]
public async Task Should_Display_Products()
{
var mockService = Substitute.For<IProductService>();
mockService.GetAllAsync(Arg.Any<CancellationToken>())
.Returns(new List<ProductDto>
{
new(1, "Widget", 9.99m),
new(2, "Gadget", 19.99m)
});
Services.AddSingleton(mockService);
var cut = RenderComponent<ProductList>();
cut.WaitForState(() => cut.FindAll(".product-card").Count > 0);
cut.FindAll(".product-card").Should().HaveCount(2);
cut.Markup.Should().Contain("Widget");
}
}
Testing Forms
[Fact]
public void Form_Validates_Required_Fields()
{
var cut = RenderComponent<CreateProductForm>();
cut.Find("form").Submit();
cut.FindAll(".validation-message").Should().NotBeEmpty();
}
[Fact]
public void Form_Submits_Valid_Data()
{
var mockService = Substitute.For<IProductService>();
Services.AddSingleton(mockService);
var cut = RenderComponent<CreateProductForm>();
cut.Find("#name").Change("New Product");
cut.Find("#price").Change("29.99");
cut.Find("form").Submit();
mockService.Received(1).CreateAsync(
Arg.Is<CreateProductRequest>(r => r.Name == "New Product"),
Arg.Any<CancellationToken>());
}
API Integration Tests
public sealed class ProductApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ProductApiTests(WebApplicationFactory<Program> factory)
{
_client = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.RemoveAll<AppDbContext>();
services.AddDbContext<AppDbContext>(opts =>
opts.UseInMemoryDatabase($"Test-{Guid.NewGuid()}"));
});
}).CreateClient();
}
[Fact]
public async Task GetProducts_ReturnsOk()
{
var response = await _client.GetAsync("/api/products");
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Theory]
[InlineData("", HttpStatusCode.BadRequest)]
[InlineData("Valid Name", HttpStatusCode.Created)]
public async Task CreateProduct_ValidatesInput(string name, HttpStatusCode expected)
{
var response = await _client.PostAsJsonAsync("/api/products",
new { Name = name, Price = 10.0 });
response.StatusCode.Should().Be(expected);
}
}
Test with Real Database (Testcontainers)
public sealed class ProductDbTests : IAsyncLifetime
{
private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder()
.WithImage("postgres:16")
.Build();
public async Task InitializeAsync() => await _postgres.StartAsync();
public async Task DisposeAsync() => await _postgres.DisposeAsync();
[Fact]
public async Task Can_Create_And_Read_Product()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseNpgsql(_postgres.GetConnectionString())
.Options;
await using var db = new AppDbContext(options);
await db.Database.EnsureCreatedAsync();
db.Products.Add(new Product { Name = "Test", Price = 9.99m });
await db.SaveChangesAsync();
var product = await db.Products.FirstAsync();
product.Name.Should().Be("Test");
}
}
More from lobbi-docs/claude
vision-multimodal
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
244design-system
Apply and manage the AI-powered design system with 50+ curated styles
126complex-reasoning
Multi-step reasoning patterns and frameworks for systematic problem solving. Activate for Chain-of-Thought, Tree-of-Thought, hypothesis-driven debugging, and structured analytical approaches that leverage extended thinking.
106gcp
Google Cloud Platform services including GKE, Cloud Run, Cloud Storage, BigQuery, and Pub/Sub. Activate for GCP infrastructure, Google Cloud deployment, and GCP integration.
73kanban
Kanban methodology including boards, WIP limits, flow metrics, and continuous delivery. Activate for Kanban boards, workflow visualization, and lean project management.
63debugging
Debugging techniques for Python, JavaScript, and distributed systems. Activate for troubleshooting, error analysis, log investigation, and performance debugging. Includes extended thinking integration for complex debugging scenarios.
59