0
votes

I am trying to get a better understanding of integration testing. By default, a lot of example utilize IClassFixture<T> for integration testing (e.g. being https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#basic-tests-with-the-default-webapplicationfactory).

That works great for testing things like - is the page loading, is the form being displayed, am I getting the correct http status code etc. But when testing an API, you'll want some seed data to exist. In order to get seed data into your testing, typical go-to is EF in-memory database. 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://docs.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?


Below is my custom web application factory that I am using for my 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();
                }
            });
        }
    }
1

1 Answers

1
votes

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