使用Moq与RestSharp进行Xunit测试:模拟分页API响应
使用Xunit + Moq + RestSharp 测试TopLevelCustomersClient
测试思路
我们需要Mock IRestClientFactory 和 IRestClient,模拟API的分页响应,验证GetAllCustomers方法能正确收集所有客户ID,且确认响应状态为成功(OK)。
首先补全原代码中缺失的依赖类型:
// 原代码中用到的响应实体类 public class CustomerResponse { public List<string> TopLevelCustomerIds { get; set; } public string Next { get; set; } } // 原代码中的接口定义 public interface ITopLevelCustomersClient { Task<List<string>> GetAllCustomers(); }
测试代码实现
1. 安装必要NuGet包
测试项目需安装以下包:
xunitxunit.runner.visualstudioMoqRestSharpNewtonsoft.Json
2. 编写测试类
using Moq; using RestSharp; using System.Net; using Newtonsoft.Json; using Xunit; using Microsoft.Extensions.Logging; namespace test.Tests { public class TopLevelCustomersClientTests { private readonly Mock<IRestClientFactory> _restClientFactoryMock; private readonly Mock<IRestClient> _restClientMock; private readonly Mock<ILogger<TopLevelCustomersClient>> _loggerMock; private readonly TopLevelCustomersClient _client; public TopLevelCustomersClientTests() { // 初始化Mock对象 _restClientMock = new Mock<IRestClient>(); _restClientFactoryMock = new Mock<IRestClientFactory>(); _loggerMock = new Mock<ILogger<TopLevelCustomersClient>>(); // 配置工厂返回Mock的RestClient _restClientFactoryMock.Setup(f => f.CreateClient(It.IsAny<string>())) .Returns(_restClientMock.Object); // 初始化待测试客户端 _client = new TopLevelCustomersClient("https://api.example.com/", _loggerMock.Object, _restClientFactoryMock.Object); } [Fact] public async Task GetAllCustomers_ShouldReturnAllIds_SinglePageResponse() { // Arrange var expectedIds = new List<string> { "CUST-001", "CUST-002" }; var responseContent = JsonConvert.SerializeObject(new CustomerResponse { TopLevelCustomerIds = expectedIds, Next = null }); var mockResponse = new RestResponse { StatusCode = HttpStatusCode.OK, IsSuccessful = true, Content = responseContent }; _restClientMock.Setup(c => c.GetAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockResponse); // Act var result = await _client.GetAllCustomers(); // Assert Assert.Equal(expectedIds.Count, result.Count); Assert.Contains("CUST-001", result); Assert.Contains("CUST-002", result); _restClientMock.Verify(c => c.GetAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>()), Times.Once); } [Fact] public async Task GetAllCustomers_ShouldCombineIds_MultiplePages() { // Arrange var page1Ids = new List<string> { "CUST-001", "CUST-002" }; var page2Ids = new List<string> { "CUST-003", "CUST-004" }; var combinedIds = page1Ids.Concat(page2Ids).ToList(); var page1Response = new RestResponse { StatusCode = HttpStatusCode.OK, IsSuccessful = true, Content = JsonConvert.SerializeObject(new CustomerResponse { TopLevelCustomerIds = page1Ids, Next = "https://api.example.com/customers?page=2" }) }; var page2Response = new RestResponse { StatusCode = HttpStatusCode.OK, IsSuccessful = true, Content = JsonConvert.SerializeObject(new CustomerResponse { TopLevelCustomerIds = page2Ids, Next = null }) }; _restClientMock.SetupSequence(c => c.GetAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>())) .ReturnsAsync(page1Response) .ReturnsAsync(page2Response); // Act var result = await _client.GetAllCustomers(); // Assert Assert.Equal(combinedIds.Count, result.Count); foreach (var id in combinedIds) { Assert.Contains(id, result); } _restClientMock.Verify(c => c.GetAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>()), Times.Exactly(2)); } [Fact] public async Task GetAllCustomers_ShouldThrowException_ResponseFailed() { // Arrange var mockResponse = new RestResponse { StatusCode = HttpStatusCode.BadRequest, IsSuccessful = false, StatusDescription = "Bad Request" }; _restClientMock.Setup(c => c.GetAsync(It.IsAny<RestRequest>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockResponse); // Act & Assert var exception = await Assert.ThrowsAsync<Exception>(() => _client.GetAllCustomers()); Assert.Contains("Error getting customers: Bad Request", exception.Message); } } }
关键配置说明
- Mock IRestClientFactory:让它返回预先配置的
IRestClient实例,避免调用真实API。 - 模拟成功响应:设置
IsSuccessful = true和StatusCode = HttpStatusCode.OK,同时序列化CustomerResponse作为响应内容,模拟API返回数据。 - 处理分页逻辑:用
SetupSequence配置GetAsync多次调用返回不同分页响应,模拟多页数据场景。 - 验证核心逻辑:断言返回的客户ID集合符合预期,同时验证
GetAsync调用次数与分页数量匹配。
内容的提问来源于stack exchange,提问作者Zordanian
相关产品推荐
相关产品推荐

