如何使用NUnit比较字典中嵌套的HashSet类型集合?
解决方案
问题原因
NUnit默认的嵌套集合相等比较仅对顶层Dictionary做了无序适配,嵌套的HashSet会被当作普通IEnumerable按迭代顺序逐项比对,所以元素插入顺序不同时会触发断言失败,不符合HashSet的无序语义。
推荐实现方案
方案1:NUnit 3+ 按类型指定嵌套比较器(推荐)
NUnit的Is.EqualTo支持针对嵌套层级的指定类型注册自定义比较器,无需手动遍历集合:
// 字符串类型HashSet场景示例 var setComparer = HashSet<string>.CreateSetComparer(); Assert.That(a2, Is.EqualTo(a1) .Using<HashSet<string>>(setComparer));
针对你提到的控制流图业务场景(Dictionary<BasicBlock, HashSet<BasicBlock>>),如果BasicBlock需要自定义相等规则,可以先实现对应比较器再注册:
// 自定义BasicBlock相等比较器(默认Equals符合业务要求可跳过该步骤) public class BasicBlockComparer : IEqualityComparer<BasicBlock> { public bool Equals(BasicBlock x, BasicBlock y) => x?.BlockId == y?.BlockId; public int GetHashCode(BasicBlock obj) => obj?.BlockId ?? 0; } // 断言写法 var bbComparer = new BasicBlockComparer(); var cfgSetComparer = HashSet<BasicBlock>.CreateSetComparer(bbComparer); Assert.That(actualCfgEdges, Is.EqualTo(expectedCfgEdges) // 可选:如果Dictionary的键BasicBlock需要自定义比较则添加该行 .Using<BasicBlock>(bbComparer) // 指定HashSet<BasicBlock>类型使用专用集合比较器 .Using<HashSet<BasicBlock>>(cfgSetComparer));
方案2:手动遍历逐对比较(兼容性更好)
如果使用的NUnit版本不支持按类型注册嵌套比较器,可以手动分两步比较,逻辑更直观:
// 1. 先校验两个字典的键完全匹配 Assert.That(actual.Keys, Is.EquivalentTo(expected.Keys)); // 2. 逐键比较对应的HashSet(Is.EquivalentTo默认无视集合顺序) foreach (var key in expected.Keys) { Assert.That(actual[key], Is.EquivalentTo(expected[key])); }
内容的提问来源于stack exchange,提问作者Sweeper
相关产品推荐
相关产品推荐

