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

xUnit Get请求测试失败:Return_Expected_Result_From_API断言不匹配问题求助

解决xUnit Get请求测试中Assert.Equal失败的问题

让我们一步步拆解你的问题:

1. 首先修复预期内容的类型错误

看你的测试代码,expectedContent是List<GetLocationDTO>,但你在里面混入了GetCountryDTO的实例:

var expectedContent = new List<GetLocationDTO>
{
    new GetLocationDTO { Id = 1, Name = "England", ShortName = "Eng" },
    new GetCountryDTO { Id = 2, Name = "France", ShortName = "Fr" }, // 类型不匹配
    new GetCountryDTO { Id = 3, Name = "Lithuania", ShortName = "LT" } // 类型不匹配
};

即使GetCountryDTO和GetLocationDTO属性结构完全一致,它们是不同的类型。API返回的内容反序列化后会得到GetLocationDTO实例,和你预期里的GetCountryDTO实例根本无法匹配。先把这两个改成GetLocationDTO:

var expectedContent = new List<GetLocationDTO>
{
    new GetLocationDTO { Id = 1, Name = "England", ShortName = "Eng" },
    new GetLocationDTO { Id = 2, Name = "France", ShortName = "Fr" },
    new GetLocationDTO { Id = 3, Name = "Lithuania", ShortName = "LT" }
};

2. 解决引用类型的相等性校验问题

这是核心问题:默认情况下,C#引用类型的Equals方法比较的是对象引用,而不是属性值。即使反序列化得到的GetLocationDTO实例和你预期的实例属性完全一致,它们是内存中的两个不同对象,Assert.Equal就会判定为不相等。

你有几个可行的解决方案:

选项A:使用FluentAssertions库(推荐)

这个库专门为测试断言设计,可以自动比较对象的属性值,用法简洁且可读性高。

  1. 安装NuGet包:FluentAssertions
  2. 修改你的断言代码:
public static async Task AssertResponseWithContentAsync<T>(Stopwatch stopwatch, HttpResponseMessage response, HttpStatusCode expectedStatusCode, T expectedContent)
{
    AssertCommonResponseParts(stopwatch, response, expectedStatusCode);
    Assert.Equal(_jsonMediaType, response.Content.Headers.ContentType?.MediaType);
    
    var actualContent = await JsonSerializer.DeserializeAsync<T?>(
        await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions);
    
    // 用FluentAssertions的属性相等断言
    actualContent.Should().BeEquivalentTo(expectedContent);
}

选项B:给GetLocationDTO重写相等性方法

让DTO自己定义“相等”的规则:

public class GetLocationDTO : IEquatable<GetLocationDTO>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string ShortName { get; set; }

    public bool Equals(GetLocationDTO? other)
    {
        if (other is null) return false;
        // 比较所有关键属性
        return Id == other.Id 
               && Name == other.Name 
               && ShortName == other.ShortName;
    }

    // 重写基类方法以保持一致性
    public override bool Equals(object? obj) => Equals(obj as GetLocationDTO);
    public override int GetHashCode() => HashCode.Combine(Id, Name, ShortName);
}

这样Assert.Equal就会按照你定义的规则比较属性值,而不是对象引用。

选项C:使用自定义比较器

如果你不想修改DTO类,可以创建一个IEqualityComparer<GetLocationDTO>,然后用SequenceEqual比较集合:

// 定义比较器
public class LocationDtoEqualityComparer : IEqualityComparer<GetLocationDTO>
{
    public bool Equals(GetLocationDTO? x, GetLocationDTO? y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;
        return x.Id == y.Id && x.Name == y.Name && x.ShortName == y.ShortName;
    }

    public int GetHashCode(GetLocationDTO obj)
    {
        return HashCode.Combine(obj.Id, obj.Name, obj.ShortName);
    }
}

// 修改断言代码
var actualContent = await JsonSerializer.DeserializeAsync<List<GetLocationDTO>>(
    await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions);

Assert.True(actualContent.SequenceEqual(expectedContent, new LocationDtoEqualityComparer()));

总结

先修正预期内容的类型错误,再选择一种适合你的方式解决引用类型的相等性问题。个人推荐FluentAssertions,它能让你的测试断言更易读,还能轻松处理复杂对象的嵌套比较。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 12:53:15