---
url: https://jasperfx.github.io/alba/guide/gettingstarted.md
description: >-
  Install Alba, bootstrap your ASP.NET Core application with AlbaHost, and write
  your first in-memory HTTP integration test scenario.
---

# Getting Started

## What is Alba?

Alba is a class library that you use in combination with unit testing tools like [xUnit.Net](https://xunit.net), [NUnit](https://docs.nunit.org/) or [TUnit](https://tunit.dev) 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](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests).

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](https://jasperfx.github.io/alba/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](https://jasperfx.github.io/alba/llms.txt) (an index
of every page) or [llms-full.txt](https://jasperfx.github.io/alba/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:

```bash
dotnet add package Alba
```

In 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](bootstrapping.md).

As an example, consider this very small ASP.NET Core application utilizing the new [Minimal API](https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?tabs=visual-studio) approach:

```cs
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);
```

snippet source | anchor

**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](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.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:

```xml
  <ItemGroup>
    <InternalsVisibleTo Include="ProjectName.Tests" />
  </ItemGroup>
```

We can now scaffold the `AlbaHost` using `AlbaHost.For<T>`, where `T` is your applications entry point:

```cs
await using var host = await AlbaHost.For<global::Program>(x =>
{
    x.ConfigureServices((context, services) =>
    {
        services.AddSingleton<IService, ServiceA>();
    });
});
```

snippet source | anchor

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](https://andrewlock.net/exploring-dotnet-6-part-6-supporting-integration-tests-with-webapplicationfactory-in-dotnet-6/)
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](xunit.md), [NUnit](nunit.md) or [TUnit](tunit.md)
:::

## 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>`.

```cs
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"));
```

snippet source | anchor

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:

```cs
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:

```cs
[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();
    });
}
```

snippet source | anchor

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:

```cs
[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);
}
```

snippet source | anchor

If the existing *Scenario* assertions aren't enough to verify your test case, you can work directly against the raw response:

```cs
[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;

}
```

snippet source | anchor

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:

```cs
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
    });
```

snippet source | anchor

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](xunit.md), [NUnit](nunit.md) and [TUnit](tunit.md).

### 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](security.md) 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](#using-an-ai-coding-agent), and point it at
[llms.txt](https://jasperfx.github.io/alba/llms.txt) or [llms-full.txt](https://jasperfx.github.io/alba/llms-full.txt)
for the full documentation.

### Is Alba free?

Yes. Alba is licensed under Apache 2.0. [JasperFx Software](https://jasperfx.net) offers paid support and consulting.
