Description
When writing integration tests for ASP.NET Core apps, it's common to use the WebApplicationFactory<T>
class to create and configure a test instance of the application. This class accepts a generic argument, the type of which is used to discover the application entry point in the type's assembly. It is often very common to (and documented as such) to use the application's Program
class as the generic argument, e.g:
public class BasicTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
public async Task Get_HomePage_ReturnSuccessAndCorrectContentType()
{
// Arrange
var client = _factory.CreateClient();
var path = "/";
// Act
var response = await client.GetAsync(path);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString());
}
}
When the ASP.NET Core application uses C# top-level statements in Program.cs
however (as is the default in ASP.NET Core project templates since .NET 6), the Program
class is internal
by default, and thus cannot be accessed by the test project without further configuration, either:
- Configuring
InternalsVisibleTo
in the ASP.NET Core project to allow the test project to see its internal types/members - Explicitly making the generated
Program
class public by addingpublic partial class Program { }
to Program.cs (or somewhere else in the project)
While another public class in the ASP.NET Core project could be used as the generic argument to WebApplicationFactory
, in some cases with top-level statements and minimal APIs there may not even be public classes in the project.
Proposal
To solve this, we should update the Web SDK to, by default, include a source generator that generates the requisite public partial class Program { }
unless the project already includes an explicit non-public Program
class declaration. This behavior should be able to be disabled by setting an MSBuild property GenerateImplicitPublicProgram
to "false"
.