Getting Started
What is Alba?
Alba is a class library that you use in combination with unit testing tools like xUnit.Net, NUnit or TUnit to author integration tests against ASP.NET Core HTTP endpoints. Alba scenarios actually exercise the full ASP.NET Core application by running HTTP requests through your ASP.NET system in memory using the built in ASP.NET Core TestServer.
You can certainly write integration tests by hand using the lower level TestServer and HttpClient, but you'll write much less code with Alba. Moreover, Alba scenarios were meant to be declarative to maximize the readability of the integration tests, making those tests much more valuable as living technical documentation.
TIP
As of 9.0+, Alba only supports .NET 10.0 or greater. You can still use older versions of Alba to test previous versions of ASP.NET Core.
Using an AI Coding Agent
If an AI coding agent (Claude Code, Copilot, Cursor, Codex and the like) is adding the tests, give it the Alba agent prompt. It condenses the setup rules, the scenario API and the common pitfalls of Alba 9 into one block of instructions.
Agents can also fetch the prompt directly from agent-prompt.md.
- Claude Code: paste the prompt into the repository's
CLAUDE.md, or into a project skill, so it loads with every session. - Other agents: put it in
AGENTS.md,.cursor/rules, or the equivalent instructions file for the tool. - One-off tasks: paste it at the top of the task description together with the endpoints you want covered.
When an agent needs more depth than the prompt, point it at llms.txt (an index of every page) or llms-full.txt (the whole documentation in one file).
Alba Setup
To get started with Alba, add a Nuget reference to the Alba library to your testing project that also references the ASP.NET Core project that you're going to be testing:
dotnet add package AlbaIn the following sections I'll show you how to bootstrap your ASP.NET Core system with Alba and start authoring specifications with the AlbaHost type.
Initializing AlbaHost
Alba supports both traditional Startup.cs projects and the WebApplicationBuilder minimal hosting approach. On .NET 10, AlbaHost.For<T> works with both models. On .NET 11, AlbaHost.For<T> requires a WebApplicationBuilder application; bootstrap a Startup.cs-style application through its IHostBuilder instead, as shown in Alternative Bootstrapping Methods.
As an example, consider this very small ASP.NET Core application utilizing the new Minimal API approach:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.MapGet("/", () => "Hello World!");
app.MapGet("/blowup", context => throw new Exception("Boo!"));
app.MapPost("/json", (MyEntity entity) => entity);
app.MapGet("/args", () => Results.Ok(args));
app.Run();
public record MyEntity(Guid Id, string MyValue);As this is a Minimal API project, you will need to allow your test project access to the internal types of your application under test. You can do that by either using the InternalsVisibleToAttribute in your main application project, or use this within the project file of your application where "ProjectName.Tests" would be your testing project name:
<ItemGroup>
<InternalsVisibleTo Include="ProjectName.Tests" />
</ItemGroup>We can now scaffold the AlbaHost using AlbaHost.For<T>, where T is your applications entry point:
await using var host = await AlbaHost.For<global::Program>(x =>
{
x.ConfigureServices((context, services) =>
{
services.AddSingleton<IService, ServiceA>();
});
});The AlbaHost.For<T>(Action<IWebHostBuilder> configuration) method uses WebApplicationFactory and all its magic static member trickery to intercept and run the implied Program.Main() method from the sample application above while also allowing you to customize the application configuration at testing time. The "T" in this case is only a marker type so that WebApplicationFactory can choose the correct entry assembly for the web application that is being tested by Alba.
See this blog post from Andrew Lock on the WebApplicationFactory mechanics for more information.
TIP
AlbaHost is an expensive object to create, so you'll generally want to reuse it across tests. See the relevant guide for xUnit, NUnit or TUnit
Configuring the Host Fluently
Every bootstrapping method returns an AlbaHostBuilder. You can chain the Alba specific parts of the setup onto it as part of the construction. ASP.NET Core host customization such as service registrations stays in the configuration action passed to AlbaHost.For<T>.
await using var host = await AlbaHost.For<WebAppSecuredWithJwt.Program>(x =>
{
// Service registrations and other ASP.NET Core host
// customization belong in this action
x.ConfigureServices(services =>
{
// services.AddSingleton<IExternalService, StubbedExternalService>();
});
})
// Override configuration values. These are visible to the
// application's own startup code
.WithConfiguration("ConnectionStrings:Postgres", "MyOverriddenValue")
// Apply Alba extensions
.WithExtension(new AuthenticationStub().WithName("jeremy"));Calls are applied in the order they are chained, so an extension chained later overrides what an earlier one registered.
Configuration values take precedence over the application's own configuration files. On .NET 11, AlbaHost.For<T> applies them to the WebApplicationBuilder as soon as it is created, before the application's own startup code runs, so a value read in Program.cs before builder.Build() already sees the override. On .NET 10, AlbaHost.For<T> passes the values to the application as host configuration, which requires the application to call WebApplication.CreateBuilder(args). Host-level keys such as environment and contentRoot cannot be changed this way, use UseEnvironment on the Action<IWebHostBuilder> overload for those.
Seeing the test from the application
By default the application handles each request without the test's async context, like a real server would. WithTestContextFlow() runs each scenario's request in the test's context instead, so values the test or the test framework keep in an AsyncLocal<T>, such as xUnit's TestContext.Current, the current Activity and the current culture, are visible to the application while it handles the request:
await using var host = await AlbaHost.For<Program>()
.WithTestContextFlow();A logger that looks up the current test for each message, such as one writing to xUnit's TestContext.Current.TestOutputHelper, then puts the application's logs in the test that sent the request.
Each request also carries the trace context of the test's current Activity, including its baggage, in the request headers regardless of WithTestContextFlow being set or not.
Running a Scenario
Once you have a AlbaHost object, you're ready to execute Scenario's through your system inside of tests. Below is a scenario for the "hello, world" application:
[Fact]
public async Task should_say_hello_world()
{
// Alba will automatically manage the lifetime of the underlying host
await using var host = await AlbaHost.For<global::Program>();
// This runs an HTTP request and makes an assertion
// about the expected content of the response
await host.Scenario(_ =>
{
_.Get.Url("/");
_.ContentShouldBe("Hello World!");
_.StatusCodeShouldBeOk();
});
}The Action<Scenario> argument will completely configure the ASP.NET HttpContext for the request and apply any of the declarative response assertions. The actual HTTP request happens inside of the Scenario() method.
The Scenario response contains the raw HttpContext and several helper methods to help you parse or read information from the response body:
[Fact]
public async Task should_return_entity_assert_response()
{
await using var host = await AlbaHost.For<global::Program>();
var guid = Guid.NewGuid();
var res = await host.Scenario(_ =>
{
_.Post.Json(new MyEntity(guid, "SomeValue")).ToUrl("/json");
_.StatusCodeShouldBeOk();
});
var json = await res.ReadAsJsonAsync<MyEntity>();
Assert.Equal(guid, json.Id);
}If the existing Scenario assertions aren't enough to verify your test case, you can work directly against the raw response:
[Fact]
public async Task should_say_hello_world_with_raw_objects()
{
await using var host = await AlbaHost.For<global::Program>();
var response = await host.Scenario(_ =>
{
_.Get.Url("/");
_.StatusCodeShouldBeOk();
});
// you can go straight at the HttpContext & do assertions directly on the responseStream
Stream responseStream = response.Context.Response.Body;
}Do note that Alba quietly "rewinds" the HttpContext.Response.Body stream so that you can more readily read and work with the contents.
Customizing the System for Testing
You can configure your application with mocked services or test-specific configuration like so:
var stubbedWebService = new StubbedWebService();
await using var host = await AlbaHost.For<global::Program>(x =>
{
// override the environment if you need to
x.UseEnvironment("Testing");
// override service registrations or internal options if you need to
x.ConfigureServices(s =>
{
s.AddSingleton<IExternalWebService>(stubbedWebService);
s.PostConfigure<MvcNewtonsoftJsonOptions>(o =>
o.SerializerSettings.TypeNameHandling = TypeNameHandling.All);
});
});
host.BeforeEach(httpContext =>
{
// do some data setup or clean up before every single test
})
.AfterEach(httpContext =>
{
// do any kind of cleanup after each scenario completes
});Alba does not do anything to set the hosting environment, but you can do that yourself via the IWebHostBuilder
Frequently Asked Questions
How is Alba different from WebApplicationFactory?
Alba builds on WebApplicationFactory and TestServer. Instead of building HttpClient requests and parsing responses by hand, you describe the request and the expected response in one scenario, and a failure reports the response body. Alba also adds authentication stubs, JSON and problem details assertions, host pooling for parallel tests and endpoint coverage reports.
Which test frameworks does Alba work with?
Any of them. The docs show how to share a host with xUnit, NUnit and TUnit.
Which versions of .NET are supported?
Alba 9 supports .NET 10 and .NET 11. Earlier versions of Alba support older versions of ASP.NET Core.
Can I test applications that use authentication?
Yes. Security extensions stub out authentication, mint JWT bearer tokens from your own configuration, or sign in against a real OpenID Connect server.
Can AI coding agents write Alba tests?
Yes. Give the agent the Alba agent prompt, and point it at llms.txt or llms-full.txt for the full documentation.
Is Alba free?
Yes. Alba is licensed under Apache 2.0. JasperFx Software offers paid support and consulting.
