Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net Core Integration Testing with CustomWebApplicationFactory - How does it work?

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.


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?


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?


E.g. 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?


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();
                }
            });
        }
    }
like image 382
Help123 Avatar asked Oct 30 '25 22:10

Help123


1 Answers

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

like image 104
Edward Avatar answered Nov 01 '25 11:11

Edward