Extensions for testing
This base class provides a convenient way to run services locally, with postgres DB run in a local container, which in turn allows to run integration tests checking controller logic or to check behaviour in an end-to-end way.
Since PostgresWebApplicationFactory<TStartup, TDatabaseContext> is an abstract class, one has to provide concrete implementation for each endpoint to be tested.
For example:
public class ServiceNamePostgresWebApplicationFactory<TStartup> : PostgresWebApplicationFactory<TStartup, DatabaseContext>
where TStartup : class
// DatabaseContext here is ServiceName-specific ef-core-based DbContext, which will be built upon pg-sql-container locally
{
protected override string GetMigrationHistoryTableName() => DatabaseContext.MigrationHistoryTable;
protected override string GetSchemaName() => DatabaseContext.SchemaName;
protected override IHostBuilder CreateHostBuilder()
{
var remoteSettingsData = new[]
{
"...",
"...",
"..."
};
return Program.CreateHostBuilder(new NullLoggerFactory(), remoteSettingsData);
// alternatively one can provide settings explicitly
}
protected override void ConfigureServicesEx(IServiceCollection services)
{
// ef-core migrations can be explicitly enabled for some enpoints,
// which don't have them in production
services.AddHostedService<MigrationHost>();
// by default all checks regarding migrations will be disabled
}
}
Having defined concrete base class, one will be able to use it as a fixture for tests:
public class SamplePostgresWebApplicationFactoryTests : IClassFixture<ServiceNamePostgresWebApplicationFactory<Startup>>
// Startup here is endpoint class for ServiceName
{
private readonly ServiceNamePostgresWebApplicationFactory<Startup> _factory;
public SamplePostgresWebApplicationFactoryTests(ServiceNamePostgresWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/api/somecontroller/somemethod")]
public async Task Get_EndpointsReturnNonEmptySuccessAndCorrectContentType(string url)
{
// Arrange
// creates http client
var client = _factory.CreateClient();
// Act
// calls controller run locally via
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
var content = await response.Content.ReadAsStringAsync();
Assert.NotNull(content);
Assert.Equal("application/json; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Please refer to the documentation on WebApplicationFactory-based integration test in AspNetCore for information on way of configuring and troubleshooting the web part of this set up: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-5.0
Testing of gRPC endpoints is also supported. Please refer to the following example:
public class SamplePostgresWebApplicationFactoryTests : IClassFixture<ServiceNamePostgresWebApplicationFactory<Startup>>
{
private readonly ServiceNamePostgresWebApplicationFactory<Startup> _factory;
private readonly ITestOutputHelper _testOutputHelper;
public SamplePostgresWebApplicationFactoryTests(
ServiceNamePostgresWebApplicationFactory<Startup> factory,
ITestOutputHelper testOutputHelper)
{
_factory = factory;
_testOutputHelper = testOutputHelper;
}
[Fact]
public async Task Grpc_Works()
{
//Arrange
using var channel = _factory.CreateGrpcChannel();
var client = new Users.UsersClient(channel);
// Act
CreateUserResponse result = null;
try
{
result = await client.CreateAsync(new CreateUserRequest()
{
TextId = "text",
RequestId = Guid.NewGuid().ToString(),
OtherAttribute = Guid.NewGuid().ToString()
});
}
catch (RpcException e)
{
_factory.ShowServerLogs(_testOutputHelper);
throw;
}
//Assert
Assert.Equal("text", result.User.TextId);
}
}
}