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

如何用NSubstitute与xUnit编写含RestSharp Response的集成测试?

关于AttachProviderService的测试问题解答

集成测试 vs 单元测试,不一样

单元测试是隔离测试单个组件,比如只测你的AttachProviderService,所有外部依赖(比如RestClient、配置、日志)都用Mock替代,聚焦验证方法逻辑是否正确。而集成测试是测试多个组件协同工作,比如你的服务要和真实的AdminRead API(或测试环境的API)一起运行,验证整个调用链路是否通顺、数据传递是否正确。简单说:单元测看「代码逻辑对不对」,集成测看「和外部服务连起来能不能正常用」。

能不能用NSubstitute Mock RestClient?

直接Mock RestClient 具体类行不通,因为它没有实现可Mock的接口。但RestSharp提供了IRestClient接口,你得先重构代码,把RestClient改成注入IRestClient,这样就能用NSubstitute Mock了。

先修改你的服务类:

public class AttachProviderService : IAttachProviderService
{
    private readonly ILogger<AttachProviderService> _logger;
    private readonly string _adminReadApi;
    private readonly IRestClient _adminReadClient;
    private readonly int _apiTimeout;

    // 把原来自己new的RestClient改成注入IRestClient
    public AttachProviderService(IConfiguration configuration, ILogger<AttachProviderService> logger, IRestClient adminReadClient)
    {
        _logger = logger;
        _adminReadApi = $"{configuration["AdminRead"]}api/SearchRead/";
        _adminReadClient = adminReadClient;
        _apiTimeout = Convert.ToInt32(configuration["Timeout"]);
    }

    // GetAttachProviderSearchList方法保持不变
}

重构后,单元测试里就能Mock IRestClient了,示例:

public class AttachProviderServiceTests
{
    [Fact]
    public async Task GetAttachProviderSearchList_ReturnsData_WhenApiCallSucceeds()
    {
        // 准备Mock依赖
        var mockLogger = Substitute.For<ILogger<AttachProviderService>>();
        var mockConfig = Substitute.For<IConfiguration>();
        mockConfig["AdminRead"].Returns("http://test-api.com/");
        mockConfig["Timeout"].Returns("30000");

        var mockRestClient = Substitute.For<IRestClient>();
        var testData = new List<AttachProviderSearchResultViewModel> { new() };
        mockRestClient.ExecuteAsync<List<AttachProviderSearchResultViewModel>>(Arg.Any<RestRequest>())
            .Returns(new RestResponse<List<AttachProviderSearchResultViewModel>> { Data = testData });

        var service = new AttachProviderService(mockConfig, mockLogger, mockRestClient);
        var searchModel = new AttachProviderSearchViewModel();

        // 调用目标方法
        var result = await service.GetAttachProviderSearchList(searchModel);

        // 验证结果
        Assert.Equal(testData, result);
        await mockRestClient.Received(1).ExecuteAsync<List<AttachProviderSearchResultViewModel>>(Arg.Any<RestRequest>());
    }
}

怎么写集成测试?

集成测试的核心是不Mock外部API,而是调用真实的(或模拟的)测试环境API,验证整个流程是否正常。

方式1:调用真实测试环境API

直接把服务指向测试环境的AdminRead API,实例化真实的RestClient,然后调用方法验证结果:

public class AttachProviderServiceIntegrationTests
{
    private readonly AttachProviderService _service;

    public AttachProviderServiceIntegrationTests()
    {
        // 配置测试用参数,指向测试环境API
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string>
            {
                { "AdminRead", "http://test-admin-api.com/" },
                { "Timeout", "30000" }
            })
            .Build();

        var logger = Substitute.For<ILogger<AttachProviderService>>();
        var realRestClient = new RestClient(config["AdminRead"]);

        _service = new AttachProviderService(config, logger, realRestClient);
    }

    [Fact]
    public async Task GetAttachProviderSearchList_ReturnsExpectedData_FromRealApi()
    {
        var searchModel = new AttachProviderSearchViewModel
        {
            // 填写测试环境API需要的搜索参数
        };

        var result = await _service.GetAttachProviderSearchList(searchModel);

        // 验证结果
        Assert.NotNull(result);
        Assert.NotEmpty(result);
        // 可根据测试API的预设响应,添加具体字段验证:Assert.Equal("TestName", result.First().Name);
    }
}

方式2:用WireMock模拟API(不依赖真实环境)

如果不想搭建测试环境API,可以用WireMock快速模拟一个临时API服务,返回预设响应:

  1. 先安装WireMock.Net NuGet包。
  2. 编写测试代码:
public class AttachProviderServiceWireMockTests : IAsyncLifetime
{
    private WireMockServer _mockServer;
    private AttachProviderService _service;

    public async Task InitializeAsync()
    {
        // 启动Mock服务器
        _mockServer = WireMockServer.Start();

        // 配置Mock的API接口和响应
        _mockServer.Given(Request.Create().WithPath("/api/SearchRead/GetAttachProviderSearchList")
                .WithMethod(HttpMethod.Post))
            .RespondWith(Response.Create()
                .WithStatusCode(HttpStatusCode.OK)
                .WithBodyAsJson(new List<AttachProviderSearchResultViewModel>
                {
                    new AttachProviderSearchResultViewModel { Id = 1, Name = "测试服务商" }
                }));

        // 配置服务参数,指向Mock服务器地址
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string>
            {
                { "AdminRead", _mockServer.Url },
                { "Timeout", "30000" }
            })
            .Build();

        var logger = Substitute.For<ILogger<AttachProviderService>>();
        var restClient = new RestClient(config["AdminRead"]);

        _service = new AttachProviderService(config, logger, restClient);
    }

    [Fact]
    public async Task GetAttachProviderSearchList_ReturnsMockedData()
    {
        var searchModel = new AttachProviderSearchViewModel();

        var result = await _service.GetAttachProviderSearchList(searchModel);

        Assert.NotNull(result);
        Assert.Single(result);
        Assert.Equal(1, result.First().Id);
        Assert.Equal("测试服务商", result.First().Name);
    }

    public async Task DisposeAsync()
    {
        _mockServer.Stop();
        _mockServer.Dispose();
    }
}

内容的提问来源于stack exchange,提问作者sudeesh mohan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 18:35:23