Photo by NASA on Unsplash

Start you Blazor app in order to debug and test it [Part 3]

Xavier Solau
YounitedTech
Published in
4 min readOct 11, 2022

--

Start your Blazor app to test

Let’s see how we can actually start the Blazor web application host to run our Playwright tests on it.

Note that you can find all source code used in this article in this repository!

Set up the Web testing host factory

To start the host we want to test, we can rely on a very useful class WebApplicationFactory<TEntryPoint> provided in the Microsoft.AspNetCore.Mvc.Testing nuget package.

Let’s install the package Microsoft.AspNetCore.Mvc.Testing.

$ dotnet add package Microsoft.AspNetCore.Mvc.Testing

Note that if you want to use the Blazor sever side hosting model, it can be started exactly the same way as WebAssembly hosted on a Dotnet host.

In the case you have a standalone WebAssembly client app to test, you will need to create a dedicated dotnet web host to serve your app but apart from that it will work the same way.

The class WebApplicationFactory is dedicated to bootstrap a web host application in memory to process all tests we need to do. In addition, it provides an easy way to replace services or whatever we could need to mock or to set up for our test purpose using the method WithWebHostBuilder.

The factory takes as type parameter a class that must be declared in the host application assembly. Basically it uses the given type to find the assembly where it can invoke the Assembly.EntryPoint to actually start the host.

Behind the scene the factory relies on DiagnosticListener. You can find out more about it in the source of HostFactoryResolver from the dotnet repository.

The fact is that the created host relies on the TestServer witch is not really exposing a HTTP end-point for our hosted application. The point is that Playwright is going to create a Web client that needs to run against a real HTTP server. It means that we have to customize the WebApplicationFactory in order to create a real Http hosted server.

Basically we are going to create our own factory based on WebApplicationFactory and override the method that create the host to make it use Kestrel. we also need to make some plumbing to properly dispose all resources at the end of our test.

public class WebTestingHostFactory<TProgram>
: WebApplicationFactory<TProgram>
where TProgram : class
{
// Override the CreateHost to build our HTTP host server.
protected override IHost CreateHost(IHostBuilder builder)
{
// Create the host that is actually used by the
// TestServer (In Memory).

var testHost = base.CreateHost(builder);
// configure and start the actual host using Kestrel.
builder.ConfigureWebHost(
webHostBuilder => webHostBuilder.UseKestrel());
var host = builder.Build();
host.Start();
// In order to cleanup and properly dispose HTTP server
// resources we return a composite host object that is
// actually just a way to intercept the StopAsync and Dispose
// call and relay to our HTTP host.
return new CompositeHost(testHost, host);
}
}

With the CompositeHost that relay calls to both test and kestrel hosts:

// Relay the call to both test host and kestrel host.
public class CompositeHost : IHost
{
private readonly IHost testHost;
private readonly IHost kestrelHost;
public CompositeHost(IHost testHost, IHost kestrelHost)
{
this.testHost = testHost;
this.kestrelHost = kestrelHost;
}
public IServiceProvider Services => this.testHost.Services; public void Dispose()
{
this.testHost.Dispose();
this.kestrelHost.Dispose();
}
public async Task StartAsync(
CancellationToken cancellationToken = default)
{
await this.testHost.StartAsync(cancellationToken);
await this.kestrelHost.StartAsync(cancellationToken);
}
public async Task StopAsync(
CancellationToken cancellationToken = default)
{
await this.testHost.StopAsync(cancellationToken);
await this.kestrelHost.StopAsync(cancellationToken);
}
}

Start the Web app

First we must add the host project as a dependency in the test project file MyAppTests.csproj.

<ItemGroup>
<ProjectReference Include="..\Server\MyBlazorApp.Server.csproj" />
</ItemGroup>

Since the WebTestingHostFactory needs a type parameter to locate the host assembly, we are going to create a class AssemblyClassLocator in the host Program.cs file in the project MyBlazorApp.Server:

// Type used to locate the host assembly.
public class AssemblyClassLocator
{}

Note that we could have used the WeatherForecastController type (or any other type in the host assembly) as the factory parameter. It is just used to get the host assembly.

With our factory, this is now easy to start and customize the app to test calling the CreateDefaultClient to actually start the host.

var url = "https://localhost:5000";// Create the host factory with the App class as parameter and the
// url we are going to use.
using var hostFactory =
new WebTestingHostFactory<AssemblyClassLocator>();
hostFactory
// Override host configuration to mock stuff if required.
.WithWebHostBuilder(builder =>
{
// Setup the url to use.
builder.UseUrls(url);
// Replace or add services if needed.
builder.ConfigureServices(services =>
{
// services.AddTransient<....>();
});
// Replace or add configuration if needed.
builder.ConfigureAppConfiguration((app, conf) =>
{
// conf.AddJsonFile("appsettings.Test.json");
});
})
// Create the host using the CreateDefaultClient method.
.CreateDefaultClient();

Note that you can customize the created host with the ConfigureServices and the ConfigureAppConfiguration methods.

--

--