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

C#单元测试如何验证HttpClient的PostAsync调用及传入参数正确性

实现单元测试的解决方案

现有代码问题

你当前无法测试PostAsync调用的核心原因是方法内部直接实例化HttpClient,没有对外暴露依赖入口,无法替换为模拟对象捕获调用记录。同时每次在方法内using创建HttpClient属于不推荐实践,会频繁占用释放端口,容易引发资源耗尽问题。

第一步:改造业务代码解耦依赖

先调整业务类,将HttpClient通过构造注入,为后续单元测试替换模拟对象提供入口:

public class YourRestServiceClass
{
    private readonly HttpClient _httpClient;

    public YourRestServiceClass(HttpClient httpClient)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public async Task<HttpResponseMessage> MakeRestCallAsync(Uri uri, string input)
    {
        using (var formData = new MultipartFormDataContent())
        {
            formData.Add(new StringContent(input), "Input");
            return await _httpClient.PostAsync(uri, formData);          
        }
    }
}

第二步:编写单元测试

你可以通过Moq框架模拟HttpMessageHandler(HttpClient底层发送请求的核心类),拦截并校验所有请求参数:

前置依赖

你需要提前安装NuGet包:Moq、Microsoft.VisualStudio.TestTools.UnitTesting

测试代码示例

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Moq.Protected;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

[TestClass]
public class RestServiceTests
{
    [TestMethod]
    public async Task MakeRestCallAsync_Should_Call_PostAsync_With_Expected_Parameters()
    {
        // 定义预期参数
        var expectedUri = new Uri("https://test.example.com/api");
        var expectedInput = "test_input";

        Uri actualRequestUri = null;
        string actualInputValue = null;

        // 模拟HttpMessageHandler,拦截请求
        var mockHandler = new Mock<HttpMessageHandler>();
        mockHandler.Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>()
            )
            .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
            .Callback<HttpRequestMessage, CancellationToken>(async (request, _) =>
            {
                // 捕获实际请求的URI
                actualRequestUri = request.RequestUri;
                // 解析表单内容获取Input参数
                if (request.Content is MultipartFormDataContent formContent)
                {
                    foreach (var part in formContent)
                    {
                        if (part.Headers.ContentDisposition.Name == "\"Input\"")
                        {
                            actualInputValue = await part.ReadAsStringAsync();
                            break;
                        }
                    }
                }
            });

        // 构造测试用的HttpClient和业务类实例
        var testHttpClient = new HttpClient(mockHandler.Object);
        var service = new YourRestServiceClass(testHttpClient);

        // 执行测试方法
        await service.MakeRestCallAsync(expectedUri, expectedInput);

        // 校验结果
        // 校验Post请求确实发送了1次
        mockHandler.Protected().Verify(
            "SendAsync",
            Times.Once(),
            ItExpr.Is<HttpRequestMessage>(r => r.Method == HttpMethod.Post),
            ItExpr.IsAny<CancellationToken>()
        );
        // 校验请求URI正确
        Assert.AreEqual(expectedUri, actualRequestUri);
        // 校验提交的Input参数正确
        Assert.AreEqual(expectedInput, actualInputValue);
    }
}

内容的提问来源于stack exchange,提问作者Polina F.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 00:06:04