我正在寻找在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);
}