How to Inject CustomWebApplicationFactory<Startup> in ASP.NET Core 3.1 Integration Tests?

Viewed 670

I'm using Microsoft's ASP.NET Core Integration Testing Article and Moq.Contrib.HttpClient .NET 5 project example, to setup an IntegrationTest Project. My actual app and test project are .NET Core 3.1. I'm getting an error in the constructor: Error

Not sure why there's an issue. Here's the test class:

using FluentAssertions;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Moq.Contrib.HttpClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace API.IntegrationTests
{
    // This shows how you can mock HttpClient at the service collection level in integration tests. Note: "factory" and
    // "client" here are part of the integration test framework, TestServer, not our mock. For more info on integration
    // tests in ASP.NET Core, see https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests
    public class ExampleTests : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        private readonly CustomWebApplicationFactory<Startup> factory;
        private readonly Mock<HttpMessageHandler> githubHandler = new Mock<HttpMessageHandler>();

        public ExampleTests(CustomWebApplicationFactory<Startup> factory)
        {
            this.factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    // This will configure the "github" named client to use our mock handler while keeping the default
                    // IHttpClientFactory infrastructure and any configurations made in Startup intact (as opposed to
                    // replacing the IHttpClientFactory implementation outright). This will work with typed clients,
                    // too. For the default (unnamed) client, use `Options.DefaultName`.
                    services.AddHttpClient("github")
                        .ConfigurePrimaryHttpMessageHandler(() => githubHandler.Object);
                });
            });
        }

        [Fact]
        public async Task GetReposTest()
        {
            // This is the integration test client used to make requests against the ASP.NET Core app under test
            var client = factory.CreateClient();

            var mockRepos = new GitHubRepository[]
            {
                new GitHubRepository()
                {
                    Name = "Foo",
                    Language = "TypeScript",
                    Stars = 39
                },
                new GitHubRepository()
                {
                    Name = "Bar",
                    Language = "C#",
                    Stars = 9001 // It's over 9000!!!
                }
            };

            var expectedResponse =
                "Foo (TypeScript, ★39)\n" +
                "Bar (C#, ★9001)\n";

            // Notice there was no need to set a BaseAddress in this test class. All of the dependency injection and the
            // AddHttpClient() in ConfigureServices() are essentially unchanged and working as they would normally.
            githubHandler.SetupRequest(HttpMethod.Get, "https://api.github.com/users/maxkagamine/repos")
                .ReturnsResponse(JsonConvert.SerializeObject(mockRepos), "application/vnd.github.v3+json");

            var response = await client.GetStringAsync("/");

            response.Should().Be(expectedResponse,
                "the app's requests should hit the mock handler now instead of the real API");
        }
    }
}

And here's the CustomWebApplicationFactory:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Linq;

namespace API.IntegrationTests
{
    public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDBContext>));

                services.Remove(descriptor);

                //services.AddDbContext<ApplicationDBContext>(options =>
                //{
                //    options.UseInMemoryDatabase("InMemoryDbForTesting");
                //});

                var sp = services.BuildServiceProvider();

                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDBContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                    db.Database.EnsureCreated();

                    //try
                    //{
                    //    Utilities.InitializeDbForTests(db);
                    //}
                    //catch (Exception ex)
                    //{
                    //    logger.LogError(ex, "An error occurred seeding the " +
                    //        "database with test messages. Error: {Message}", ex.Message);
                    //}
                }
            });
        }
    }
}
0 Answers
Related