In order to get a better understanding of integration testing, I use IClassFixture<T> (from https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#basic-tests-with-the-default-webapplicationfactory).
This works great for testing things like - page loading, form being displayed, getting the correct http status code, etc. But when testing an API, you'll want some seed data. To do so EF in-memory database is the typical approach. This is achieved via a custom web application factory where you can create a scope, request the appropriate service (i.e. dbcontext), and seed it (e.g. being https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#customize-webapplicationfactory).
I have an integration test project that is working and fully functional. But the nuances of how it works is still confusing to me.
This is my implementation of web application factory for integration testing. My API has basic authentication for most endpoints so I added a global filter to bypass that. But what I am doing below is essentially the same in my Program.cs in my actual API (the only difference being I don't add the fake user and global anonymous filter). So I am lead to believe that my above point stands true. Is this a correct assumption?
Another point I wanted to verify is that in an actual unit test, I can replace a service with a mock. Is this possible in an integration test where I can swap out the DI instance for a requested service to be a test service instead?
IUploadFileToAzure service. Instead of using UploadFileToAzure as the DI instance, can I replace that implementation with a TestUploadFileToAzure service in my integration test?Registering a service multiple times takes the last registration of the service so I was wondering if that can be used as a workaround for my above point. Is this even recommended? I understand it defeats the purpose of testing a service but wanted to verify if that was possible. I tried testing this locally and it did not work.
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost
.CreateDefaultBuilder<Startup>(new string[0])
.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter, AddCustomMiddlewareStartupFilter>();
});
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.UseEnvironment("Development")
.ConfigureServices(services =>
{
services.AddMvc(opt =>
{
//add a global anonymous filter
opt.Filters.Add(new AllowAnonymousFilter());
//add a filter for adding a fake claimsprincipal so that the user service
//correctly identifies the user
opt.Filters.Add(new FakeClaimsPrincipalFilter(true, false));
});
services.AddEntityFrameworkInMemoryDatabase();
// Create a new service provider.
var provider = services
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context using an in-memory
// database for testing.
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("TestDb");
options.UseInternalServiceProvider(provider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database context
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var apiDb = scopedServices.GetRequiredService<AppDbContext>();
// Ensure the database is created.
apiDb.Database.EnsureCreated();
}
});
}
}
Am I correct in assuming that when you create a CustomWebApplicationFactory, essentially you are creating a custom "Program.cs" (i.e. the typical entry point into the application) where you are free to add in additional testing services/filters as needed?
Yes, you are right. For Program.cs it will create the real host server. For CustomWebApplicationFactory, it will create TestServer for the integration tests.
my app has a IUploadFileToAzure service. Instead of using UploadFileToAzure as the DI instance, can I replace that implementation with a TestUploadFileToAzure service in my integration test?
For replacing exsting service, you could try ConfigureTestServices and you could refer Inject mock services
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