单元测试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:
这种写法下,两个独立实例只要所有属性值一致,相等判断就会返回true。public record MyClass { public int Id { get; set; } public string Name { get; set; } public int Value { get; set; } }
内容的提问来源于stack exchange,提问作者Alex King
相关产品推荐
相关产品推荐

