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



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

假设我有一个服务集合,它有一个注册的类型化HttpClient,就像这样

var serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient<ITestApiClient, TestApiClient>();
var services = serviceCollection.BuildServiceProvider();

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();
}
}

我正在寻找一种方法来注入模拟HttpClient实现到TestApiClient使用ServiceCollection而不直接实例化TestApiClient

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

var fooMock = new Mock<IFoo>();
services.AddSingleton(fooMock);

请注意,我已经找到了一种方法来模拟HttpClient与Moq和库,如RichardSzalay.MockHttp。我的问题是如何在ServiceCollection (Mocking HttpClientFactory?)中替换类型化的Httpclient。

下面是我如何解决在asp.net 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);
});
}
}

模拟的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);
}