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?
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.
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:
"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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With