.net core 集成测试中的模拟类型 HttpClient

use*_*874 5 c# asp.net-core

我正在寻找一些在 Asp.net core 应用程序的集成测试中模拟 Typed HttpClient 的想法。

假设我有一个带有注册的 Typed HttpClient 的服务集合,如下所示

var serviceCollection = new ServiceCollection();

serviceCollection.AddHttpClient<ITestApiClient, TestApiClient>();

var services = serviceCollection.BuildServiceProvider();
Run Code Online (Sandbox Code Playgroud)

TestApiClient这样的定义

public class TestApiClient: ITestApiClient
{
    HttpClient _httpClient;
    public TestApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public void MakeTestApiCall()
    {
        var response = _httpClient.GetAsync("https://reqres.in/api/users?page=2");
        response.Result.Content.ReadAsStringAsync().Result.Dump();
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种方法,将模拟HttpClient实现注入到TestApiClient使用 ServiceCollection 中,而无需直接实例化TestApiClient

类似于我们在下面的集成测试中如何用模拟的 Foo 实现替换真实的 Foo 实现。

var fooMock = new Mock<IFoo>();
services.AddSingleton(fooMock);
Run Code Online (Sandbox Code Playgroud)

请注意,我已经找到了一种使用 Moq 和RichardSzalay.MockHttp. 我的问题是如何替换 ServiceCollection 中类型化的 Httpclient(模拟 HttpClientFactory?)。

J.L*_*cos 1

以下是我如何解决在 aspnet core 6 集成测试中模拟 HttpClient 的问题:

我为集成测试创建了一个自定义 WebApplicationFactory,其中使用模拟的 HttpMessageHandler 将 HttpClient 服务替换为新客户端。

internal class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    public Mock<HttpMessageHandler> handlerMock { get; private set; }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // Remove existing IHttpClientFactory and HttpClient
            var httpClientFactoryDescriptor = services.SingleOrDefault(d => d.ServiceType ==
                    typeof(IHttpClientFactory));
            services.Remove(httpClientFactoryDescriptor);

            var httpClientDescriptor = services.SingleOrDefault(d => d.ServiceType ==
                    typeof(HttpClient));
            services.Remove(httpClientDescriptor);

            // Create a mock HttpMessageHandler
            this.handlerMock = new Mock<HttpMessageHandler>();

            // Create an HttpClient using the mocked handler
            var client = new HttpClient(this.handlerMock.Object);
            var clientFactoryMock = new Mock<IHttpClientFactory>();
            clientFactoryMock
                .Setup(f => f.CreateClient(It.IsAny<string>()))
                .Returns(client);

            // Add the new HttpClient as singleton
            services.AddSingleton<IHttpClientFactory>(clientFactoryMock.Object);
            services.AddSingleton<HttpClient>(client);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

模拟的 HttpMessageHandler 作为公共属性公开,可供测试类访问,我可以在其中进行设置:

    [Fact]
    public async Task CallApiTest()
    {
        // Arrange
        var factory = new CustomWebApplicationFactory();
        var client = factory.CreateClient();

        factory.handlerMock
               .Protected()
               .Setup<Task<HttpResponseMessage>>(
                   "SendAsync",
                   ItExpr.Is<HttpRequestMessage>(r => r.Method == HttpMethod.Get),
                   ItExpr.IsAny<CancellationToken>())
               .ReturnsAsync(new HttpResponseMessage
               { 
                   StatusCode = HttpStatusCode.OK,
                   Content = new StringContent("[]") 
               });

        // Act
        var result = await client.GetAsync("/callApi");

        // Assert
        result.StatusCode.Should().Be(HttpStatusCode.OK);

    }
Run Code Online (Sandbox Code Playgroud)