Skip to content

Integrating Alba with xUnit.Net

If you are writing only a few Alba specifications in your testing project and your application spins up very quickly, you can just happily write tests like this:

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

Do note that your [Fact] method needs to be declared as async Task to ensure that xUnit finishes the specification before disposing the system or you'll get unusual behavior. Also note that you really need to dispose the AlbaHost to shut down your application and dispose any internal services that might be holding on to computer resources.

Class Fixtures

If your application startup time becomes a performance problem, and especially in larger test suites, you probably want to share the AlbaHost object between tests. xUnit helpfully provides the class fixture feature for just this use case.

Build out your AlbaHost in a class like this:

cs
public class WebAppFixture : IAsyncLifetime
{
    public IAlbaHost AlbaHost = null!;

    public async Task InitializeAsync()
    {
        AlbaHost = await Alba.AlbaHost.For<WebApp.Program>(builder =>
        {
            // Configure all the things
        });
    }

    public async Task DisposeAsync()
    {
        await AlbaHost.DisposeAsync();
    }
}
public class WebAppFixture : IAsyncLifetime
{
    public IAlbaHost AlbaHost = null!;

    public async Task InitializeAsync()
    {
        AlbaHost = await Alba.AlbaHost.For<WebApp.Program>(builder =>
        {
            // Configure all the things
        });
    }

    public async Task DisposeAsync()
    {
        await AlbaHost.DisposeAsync();
    }
}

snippet source | anchor

Then in your actual xUnit fixture classes, implement the IClassFixture<T> class like this:

cs
public class ContractTestWithAlba : IClassFixture<WebAppFixture>
{
    public ContractTestWithAlba(WebAppFixture app)
    {
        _host = app.AlbaHost;
    }

    private readonly IAlbaHost _host;
public class ContractTestWithAlba : IClassFixture<WebAppFixture>
{
    public ContractTestWithAlba(WebAppFixture app)
    {
        _host = app.AlbaHost;
    }

    private readonly IAlbaHost _host;

snippet source | anchor

Collection Fixtures

In the previous section, the WebAppFixture instance will only be shared between all the tests in the one ContractTestWithAlba class. To reuse the IAlbaHost across multiple test fixture classes, you'll need to use xUnit.Net's Collection Fixture concept.

Still using WebAppFixture, we'll now need to have a marker collection class like this:

cs
[CollectionDefinition("scenarios")]
public class ScenarioCollection : ICollectionFixture<WebAppFixture>
{
    
}
[CollectionDefinition("scenarios")]
public class ScenarioCollection : ICollectionFixture<WebAppFixture>
{
    
}

snippet source | anchor

As a convenience, I like to have a base class for all test fixture classes that will be using scenarios like this:

cs
[Collection("scenarios")]
public abstract class ScenarioContext
{
    protected ScenarioContext(WebAppFixture fixture)
    {
        Host = fixture.AlbaHost;
    }

    public IAlbaHost Host { get; }
}
[Collection("scenarios")]
public abstract class ScenarioContext
{
    protected ScenarioContext(WebAppFixture fixture)
    {
        Host = fixture.AlbaHost;
    }

    public IAlbaHost Host { get; }
}

snippet source | anchor

And then inherit from that ScenarioContext base class in actual test fixture classes:

cs
public class sample_integration_fixture : ScenarioContext
{
    public sample_integration_fixture(WebAppFixture fixture) : base(fixture)
    {
    }
    
    [Fact]
    public Task happy_path()
    {
        return Host.Scenario(_ =>
        {
            _.Get.Url("/fake/okay");
            _.StatusCodeShouldBeOk();
        });
    }
}
public class sample_integration_fixture : ScenarioContext
{
    public sample_integration_fixture(WebAppFixture fixture) : base(fixture)
    {
    }
    
    [Fact]
    public Task happy_path()
    {
        return Host.Scenario(_ =>
        {
            _.Get.Url("/fake/okay");
            _.StatusCodeShouldBeOk();
        });
    }
}

snippet source | anchor