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

C# Xunit中使用Theory MemberData时如何断言对象列表?

实现方案(分3种场景按需选择)

场景1:保持当前逻辑,每个DTO单独发起请求

你的现有逻辑是遍历marketingLeadDtos逐个发POST请求,只需让每个请求的错误和expectedErrorMessages按索引一一对应即可,实现步骤:

  1. 先定义适配接口返回结构的响应类(已经有可跳过)
// 字段名要和你接口实际返回的字段对齐
public class ApiResponse
{
    public bool IsSuccess { get; set; }
    public List<string> Errors { get; set; } = new();
}
  1. 调整测试方法的断言逻辑,统一收集所有实际返回的错误后再批量断言:
[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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:15:10