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

单元测试Assert.Equal比较属性相同自定义类实例失败如何修复

问题根因

C#中自定义class属于引用类型,默认继承自object的Equals()方法实现的是引用相等判定:只有两个变量指向内存堆中同一个对象实例时,才会返回true。该场景里expected和actual是两个独立new出来的实例,哪怕所有属性值完全一致,因为引用地址不同,默认相等判断必然返回false。
测试框架输出报错信息时,是通过反射读取两个实例的所有属性值拼接展示,不会体现引用地址差异,所以肉眼看两个对象的输出完全一致,但断言依然失败。Assert.True(actual.Equals(expected))失败也是完全相同的原因。

可行解决方案

可根据自身场景选择以下任意一种方案:

  • 方案1:为自定义类重写Equals和GetHashCode方法
    重写后类的相等判断会改为自定义的值语义,只要匹配的属性值一致就判定相等,示例代码:
    public class MyClass
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    
        public override bool Equals(object obj)
        {
            if (obj is not MyClass other) return false;
            return Id == other.Id 
                && Name == other.Name 
                && Value == other.Value;
        }
    
        public override int GetHashCode()
        {
            return HashCode.Combine(Id, Name, Value);
        }
    }
    
    重写完成后Assert.Equal(expected, actual)和actual.Equals(expected)都会按属性值匹配逻辑正常判定。
  • 方案2:使用测试框架的深度属性比较断言,不修改原类代码
    如果不想改动生产代码的相等逻辑,可以直接用测试框架自带的逐属性比较能力,以常用的xUnit为例,直接替换断言方法为Assert.Equivalent(expected, actual)即可,这个方法会自动递归对比两个对象的所有公共属性值,不需要类重写Equals,属性值全匹配就会判定通过。
    如果使用NUnit,可使用Assert.That(actual, Is.EqualTo(expected).UsingPropertiesComparer())实现相同效果。
  • 方案3:将类改为record类型(适用于C# 9+版本)
    C# 9引入的record类型默认自动生成基于所有属性值的Equals和GetHashCode实现,天生具备值相等语义,不需要手动重写方法。可以直接把MyClass定义为record:
    public record MyClass
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    }
    
    这种写法下,两个独立实例只要所有属性值一致,相等判断就会返回true。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 04:45:38