如何让Moq模拟对象具备实际类的IEqualityComparer效果?
问题解决:Moq模拟对象在Linq.Except中自定义相等校验失效的处理
问题根源
Moq通过Mock.Of创建的动态代理对象,默认采用引用相等判断对象是否相同,即便你Setup了属性返回值,也不会自动沿用自定义的相等逻辑(比如类本身重写的Equals/GetHashCode,或自定义IEqualityComparer)。而手动实例化的对象会执行你定义的相等规则,因此两者在Except中的表现不一致。
解决方案
方案1:显式传入自定义IEqualityComparer
如果你已经实现了IEqualityComparer<SomeClass>,直接在调用Except时传入该比较器实例即可。比较器基于属性(如Account)判断相等,不受对象是否为Moq代理的影响:
[<Test>] let ``similar object should diff to none`` () = let list1 = createMockList () let list2 = createMockList () // 传入自定义比较器 let diff = list1.Except(list2, new MyCustomEqualityComparer()) Assert.That(diff, Is.Empty, "similar lists should diff to zero")
方案2:Setup Moq对象的Equals和GetHashCode方法
在创建Mock对象时,额外SetupEquals和GetHashCode方法,让它们遵循自定义的相等逻辑(比如基于Account属性):
let createMockList (): SomeClass array = let setupMock account = let mock = Mock.Of<SomeClass>(fun o -> o.Account = account) let mockInstance = Mock.Get(mock) // Setup Equals:当对方Account与当前一致时返回true mockInstance.Setup(fun o -> o.Equals(It.IsAny<SomeClass>())) .Returns(fun (other: SomeClass) -> other <> null && other.Account.Equals(account, StringComparison.OrdinalIgnoreCase) ) // Setup GetHashCode:基于Account生成哈希值 mockInstance.Setup(fun o -> o.GetHashCode()) .Returns(account.GetHashCode()) mock let sr1 = setupMock "hello" let sr2 = setupMock "world" let sr3 = setupMock "bye" // 修正原代码中的SentinelRow笔误 [|sr1;sr2;sr3|]
这样Moq对象的相等判断逻辑会和手动实例的一致,调用Except时无需额外传入比较器即可正常工作。
注意事项
- 原代码中
Mock.Of<SentinelRow>是笔误,需改为Mock.Of<SomeClass>保证类型匹配。 - 如果
SomeClass的Equals方法是非虚方法,Moq无法拦截并Setup,此时只能采用方案1(传入自定义比较器)。
内容的提问来源于stack exchange,提问作者Tri
相关产品推荐
相关产品推荐

