Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use WebApplicationFactory in .net6 (without speakable entry point)

In ASP.NET Core 6 default template moves everything from Sturtup.cs into Program.cs, and uses top-level statements in Program.cs, so there's no more (speakable) Program class ether.

That looks awesome, but now, I need to test all of this. WebApplicationFactory<T> still expects me to pass entry-point-class, but I cannot do this (due to it's name now being unspeakable).

How integration tests are expected to be configured in ASP.NET Core 6?

like image 555
maxc137 Avatar asked Jan 24 '23 06:01

maxc137


2 Answers

The problem is was solved on ASP.NET Core RC1, but as of now (September 20, 2021) the docs are incomplete.

The compiler generates a Program class behind the scenes that can be used with WebApplicationFactory<>. The class isn't public though so the InternalsVisibleTo project setting should be used.

Damien Edwards' Minimal API sample uses the latest nightly bits. The test web app class is declared as :

internal class PlaygroundApplication : WebApplicationFactory<Program>
{
    private readonly string _environment;

    public PlaygroundApplication(string environment = "Development")
    {
        _environment = environment;
    }

    protected override IHost CreateHost(IHostBuilder builder)
    {
    ...

In the application project file,InternalsVisibleTo is used to make the Program class visible to the test project:

  <ItemGroup>
    <InternalsVisibleTo Include="MinimalApiPlayground.Tests" />
  </ItemGroup>

RC1 is feature complete and, judging by previous major versions, it will probably be the first version to have a Go Live license, which means it's supported in production.

like image 86
Panagiotis Kanavos Avatar answered Apr 26 '23 07:04

Panagiotis Kanavos


Note that if you are trying to use xUnit and its IClassFixture<T> pattern, you will run into problems if you just use the InternalsVisibleTo approach. Specifically, you'll get something like this:

enter image description here

"Inconsistent accessibility: base class WebApplicationFactory<Program> is less accessible than class CustomWebApplicationFactory."

Of course you can solve this by making CustomWebApplicationFactory internal but it only moves the problem as now your unit test class will give the same error. When you try to change it there, you will find that xUnit requires that tests have a public constructor (not an internal one) and you'll be blocked.

The solution that avoids all of this and allows you to still use IClassFixture<Program> is to make the Program class public. You can obviously do this by getting rid of the magic no class version of Program.cs, but if you don't want to completely change that file you can just add this line:

public partial class Program { } // so you can reference it from tests

Of course once it's public you can use it from your test project and everything works.

As an aside, the reason why you typically want to prefer using IClassFixture is that it allows you to set up your WebApplicationFactory just once in the test class constructor, and grab an HttpClient instance from it that you can store as a field. This allows all of your tests to be shorter since they only need to reference the client instance, not the factory.

Example:

public class HomePage_Get : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client = new HttpClient();

    public HomePage_Get(CustomWebApplicationFactory factory)
    {
        _client = factory.CreateClient();
    }
    [Fact]
    public async Task IncludesWelcome()
    {
        HttpResponseMessage response = await _client.GetAsync("/");
        response.EnsureSuccessStatusCode();
        string stringResponse = await response.Content.ReadAsStringAsync();
        Assert.Contains("Welcome.", stringResponse);
    }
}

Finally note that Damian Edwards' MinimalAPIPlayground was updated to use this approach after we discussed the issue. See this commit

like image 35
ssmith Avatar answered Apr 26 '23 07:04

ssmith