You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET Core 7中模拟404错误测试关联Web API服务

问题场景与需求

现有两个ASP.NET Core 7 Web API服务:

  1. Sensor API:返回0-100之间的随机整数,代码如下:
[ApiController]
[Route("[controller]")]
public class TrafficSensorController : ControllerBase
{
    [HttpGet(Name = "GetTrafficSensor")]
    public int Get()
    {
        return new Random().Next(100);
    }
}
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:05:34