如何在ASP.NET Core 7中模拟404错误测试关联Web API服务
问题场景与需求
现有两个ASP.NET Core 7 Web API服务:
- Sensor API:返回0-100之间的随机整数,代码如下:
[ApiController] [Route("[controller]")] public class TrafficSensorController : ControllerBase { [HttpGet(Name = "GetTrafficSensor")] public int Get() { return new Random().Next(100); } }
- Report API:向Sensor API发起请求,根据返回值返回对应的交通流量描述,代码如下:
[ApiController] [Route("[controller]")] public class TrafficReportController : ControllerBase { private static readonly string SENSOR_URL = "https://localhost:7272/TrafficSensor"; [HttpGet(Name = "GetTrafficReport")] public async Task<string> Get() { var response = await new HttpClient().GetAsync(SENSOR_URL); string report = "ERROR!"; if (response.IsSuccessStatusCode) { int sensorValue = int.Parse(await response.Content.ReadAsStringAsync()); if (sensorValue < 30) report = "LIGHT TRAFFIC"; else if (sensorValue < 70) report = "MODERATE TRAFFIC"; else report = "HEAVY TRAFFIC"; } return report; } }
需要编写集成测试,模拟Sensor API不可用(返回404或500状态码)的场景,验证Report API是否能正确返回ERROR!。
实现步骤
1. 改造Report API的HttpClient注入方式
原代码直接在方法内new HttpClient(),无法进行模拟替换,需要改为依赖注入的方式,方便测试时替换为模拟客户端:
[ApiController] [Route("[controller]")] public class TrafficReportController : ControllerBase { private readonly HttpClient _httpClient; private readonly string _sensorUrl; // 通过构造函数注入HttpClient,并从配置读取Sensor API地址 public TrafficReportController(HttpClient httpClient, IConfiguration configuration) { _httpClient = httpClient; _sensorUrl = configuration["SensorApiUrl"] ?? "https://localhost:7272/TrafficSensor"; } [HttpGet(Name = "GetTrafficReport")] public async Task<string> Get() { var response = await _httpClient.GetAsync(_sensorUrl); string report = "ERROR!"; if (response.IsSuccessStatusCode) { int sensorValue = int.Parse(await response.Content.ReadAsStringAsync()); if (sensorValue < 30) report = "LIGHT TRAFFIC"; else if (sensorValue < 70) report = "MODERATE TRAFFIC"; else report = "HEAVY TRAFFIC"; } return report; } }
同时在Program.cs中注册HttpClient:
builder.Services.AddHttpClient<TrafficReportController>();
2. 编写集成测试项目
创建一个xUnit测试项目,引入Microsoft.AspNetCore.Mvc.Testing包,用于托管Report API并模拟HttpClient。
测试类实现(使用Moq简化模拟)
先安装Moq.Contrib.HttpClient包,然后编写测试代码:
using Microsoft.AspNetCore.Mvc.Testing; using Moq.Contrib.HttpClient; using Xunit; public class TrafficReportControllerTests : IClassFixture<WebApplicationFactory<Program>> { private readonly WebApplicationFactory<Program> _factory; public TrafficReportControllerTests(WebApplicationFactory<Program> factory) { _factory = factory; } [Theory] [InlineData(System.Net.HttpStatusCode.NotFound)] // 404场景 [InlineData(System.Net.HttpStatusCode.InternalServerError)] // 500场景 public async Task Get_WhenSensorApiUnavailable_ReturnsError(System.Net.HttpStatusCode errorCode) { // 创建模拟HttpClient,指定Sensor API地址返回错误状态码 var mockHandler = new Mock<HttpMessageHandler>(); mockHandler.SetupRequest(HttpMethod.Get, "https://localhost:7272/TrafficSensor") .ReturnsResponse(errorCode); // 配置测试服务器使用模拟的HttpClient var client = _factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // 替换原有的HttpClient为模拟客户端 services.AddHttpClient<TrafficReportController>() .ConfigurePrimaryHttpMessageHandler(() => mockHandler.Object); }); }).CreateClient(); // 调用Report API并验证结果 var response = await client.GetAsync("/TrafficReport"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); Assert.Equal("ERROR!", result); } }
3. 无第三方包的替代方案
如果不想引入Moq相关包,可以手动实现自定义HttpMessageHandler:
public class MockHttpMessageHandler : HttpMessageHandler { private readonly HttpStatusCode _statusCode; public MockHttpMessageHandler(HttpStatusCode statusCode) { _statusCode = statusCode; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return Task.FromResult(new HttpResponseMessage(_statusCode)); } }
测试中替换为这个handler:
[Theory] [InlineData(System.Net.HttpStatusCode.NotFound)] [InlineData(System.Net.HttpStatusCode.InternalServerError)] public async Task Get_WhenSensorApiUnavailable_ReturnsError(System.Net.HttpStatusCode errorCode) { var handler = new MockHttpMessageHandler(errorCode); var client = _factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { services.AddHttpClient<TrafficReportController>() .ConfigurePrimaryHttpMessageHandler(() => handler); }); }).CreateClient(); var response = await client.GetAsync("/TrafficReport"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); Assert.Equal("ERROR!", result); }
内容的提问来源于stack exchange,提问作者Burak TARHANLI
相关产品推荐
相关产品推荐

