C# Xunit中使用Theory MemberData时如何断言对象列表?
实现方案(分3种场景按需选择)
场景1:保持当前逻辑,每个DTO单独发起请求
你的现有逻辑是遍历marketingLeadDtos逐个发POST请求,只需让每个请求的错误和expectedErrorMessages按索引一一对应即可,实现步骤:
- 先定义适配接口返回结构的响应类(已经有可跳过)
// 字段名要和你接口实际返回的字段对齐 public class ApiResponse { public bool IsSuccess { get; set; } public List<string> Errors { get; set; } = new(); }
- 调整测试方法的断言逻辑,统一收集所有实际返回的错误后再批量断言:
[Theory, MemberData(nameof(ClientIsEmptyOrNull_Data))] public async Task When_ClientIsEmptyOrNull_Then_ReturnErrorMessage(List<MarketingLeadDto> marketingLeadDtos, List<string> expectedErrorMessages) { using (var client = new HttpClient() { BaseAddress = new Uri(TestSettings.BaseAddress) }) { _output.WriteLine("authToken: {0}", _authToken); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _authToken); // 存储所有接口返回的实际错误 var actualErrorMessages = new List<string>(); foreach (var marketingLeadDto in marketingLeadDtos) { var encodedContent = new StringContent(JsonConvert.SerializeObject(marketingLeadDto), Encoding.UTF8, "application/json"); var response = await client.PostAsync("/ABC/Leads", encodedContent); var responseString = await response.Content.ReadAsStringAsync(); _output.WriteLine("response: {0}", responseString); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // 反序列化拿到接口返回的错误信息 var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseString); actualErrorMessages.AddRange(apiResponse.Errors); } // 断言:实际错误和预期完全匹配,顺序不敏感用BeEquivalentTo,顺序敏感用Equal actualErrorMessages.Should().BeEquivalentTo(expectedErrorMessages); // 不用FluentAssertions的原生Xunit写法: // Assert.Equal(expectedErrorMessages.OrderBy(e=>e), actualErrorMessages.OrderBy(e=>e)); } }
场景2:更符合Xunit最佳实践的写法(推荐)
当前你把多个测试用例塞到一个List里传,出错后很难定位是哪个DTO的问题,建议调整TheoryData结构,每个测试用例对应单个DTO和对应的预期错误,Xunit会自动为每个用例单独执行,失败用例一目了然:
// 测试方法参数改为单个DTO+对应预期错误列表 [Theory, MemberData(nameof(ClientIsEmptyOrNull_Data))] public async Task When_ClientIsEmptyOrNull_Then_ReturnErrorMessage(MarketingLeadDto marketingLeadDto, List<string> expectedErrorMessages) { using (var client = new HttpClient() { BaseAddress = new Uri(TestSettings.BaseAddress) }) { _output.WriteLine("authToken: {0}", _authToken); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _authToken); var encodedContent = new StringContent(JsonConvert.SerializeObject(marketingLeadDto), Encoding.UTF8, "application/json"); var response = await client.PostAsync("/ABC/Leads", encodedContent); var responseString = await response.Content.ReadAsStringAsync(); _output.WriteLine("response: {0}", responseString); Assert.Equal(HttpStatusCode.OK, response.StatusCode); var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseString); apiResponse.Errors.Should().BeEquivalentTo(expectedErrorMessages); } } // 修改测试数据构造,每个用例单独传入,可无限扩展测试场景 public static TheoryData<MarketingLeadDto, List<string>> ClientIsEmptyOrNull_Data() { return new TheoryData<MarketingLeadDto, List<string>> { // 第一个测试用例:Client为空 { new MarketingLeadDto { AccountType = "1", Salutations = "1", Name = "Steven", CompanyName = "", Nationality = "", Client = "", }, new List<string> { "Client type is required" } }, // 第二个测试用例:Nationality为空 { new MarketingLeadDto { AccountType = "1", Salutations = "1", Name = "Steven", CompanyName = "test", Nationality = "", Client = "C端", }, new List<string> { "Nationality is required" } } }; }
场景3:接口支持批量提交所有DTO的场景
如果你的接口是一次性接收整个DTO列表,不需要循环发请求,直接把整个marketingLeadDtos序列化后发一次POST,然后直接断言返回的错误列表和预期一致即可,不需要额外收集错误。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

