Getting started

StoveDotnet starts once for a test run and creates an isolated test scope for every call to stove.Test. Begin with only the application and dependencies that the behavior under test actually uses.

Requirements

  • .NET 10 SDK
  • Docker or Podman for container-backed modules
  • An existing ASP.NET Core application or Generic Host worker

Your ASP.NET Core entry point must be visible to the test project:

public partial class Program;

Install the packages

Add the core package, an application host, and the systems your application uses. Packages are currently preview releases, so include --prerelease when necessary.

dotnet add package StoveDotnet --prerelease
dotnet add package StoveDotnet.AspNetCore --prerelease
dotnet add package StoveDotnet.Http --prerelease
dotnet add package StoveDotnet.Postgres --prerelease

Start Stove once

Create the fixture from your test framework's once-per-run hook. Map each exposed connection value to the same configuration key the application reads in production.

using StoveDotnet;

Stove stove = await StoveBuilder.Create()
    .WithTelemetry()
    .WithPostgres(options =>
    {
        options.ConfigureExposedConfiguration = postgres =>
            [new("ConnectionStrings:Orders", postgres.ConnectionString)];
    })
    .WithHttpClient()
    .WithAspNetCoreApplication<Program>()
    .StartAsync();

Register the application last by convention. Stove starts dependencies first, gathers their configuration, and then starts the application with those values at the highest configuration precedence.

Write a test

Every test body runs through stove.Test. The context exposes only systems registered by the fixture.

[Fact]
public Task Creates_an_order() => stove.Test(async t =>
{
    var response = (await t.Http().Post<OrderDto>(
        "/orders",
        new { productId = "chair" }))
        .Expect(HttpStatusCode.Created);

    await t.Postgres().ShouldQuery(
        "select status from orders where id = @id",
        row => row.GetString(0),
        rows => Assert.Equal(["Created"], rows),
        new NpgsqlParameter("id", response.Body.Id));
});

Dispose the shared Stove instance from the matching once-per-run teardown hook.

Add asynchronous behavior

Use the broker module that matches the application. Assertions wait only when the expected behavior is asynchronous; direct database and broker-state APIs remain explicit.

await t.Kafka().ShouldBePublished<OrderCreated>(
    message => message.Value.OrderId == response.Body.Id);

For scheduled messages and passwordless Azure namespaces, continue with Azure Service Bus. For correlation, cancellation and parallel-test tradeoffs, read Practical testing.

Pick your test framework

Stove ships no framework-specific attributes or runners. The test-framework guide shows the once-per-run lifecycle for xUnit, NUnit, MSTest and TUnit, plus isolated package-consumer examples.