如何测试范围检查所有分支以实现100%分支覆盖率?
在追求100%分支覆盖率的过程中,测试这段Solidity范围检查逻辑时遇到了分支未覆盖的问题:
// Find the matching tier for (uint256 i = 0; i < tiers.length; i++) { if (tiers[i].minVal() <= cumReceivedInvestments && cumReceivedInvestments < tiers[i].maxVal()) { return tiers[i]; } }
LCOV显示存在未覆盖分支。目前已覆盖False&&True、True&&True、False&&False三种分支情况,但无法覆盖True&&False分支——因为Tier对象要求maxVal必须大于minVal,当第一个条件tiers[i].minVal() <= cumReceivedInvestments为真时,第二个条件cumReceivedInvestments < tiers[i].maxVal()必然也为真,根本不存在触发True&&False的业务场景。尝试把范围判断逻辑拆成独立函数,也没解决覆盖问题。
可行解决方案
1. 拆分if条件,构造临时非法测试用例
把短路与(&&)拆成两层独立if判断:
// Find the matching tier for (uint256 i = 0; i < tiers.length; i++) { if (tiers[i].minVal() <= cumReceivedInvestments) { if (cumReceivedInvestments < tiers[i].maxVal()) { return tiers[i]; } } }
这种写法下,你可以在测试时临时绕过Tier的maxVal>minVal约束(比如构造一个maxVal <= minVal的非法Tier实例),构造出cumReceivedInvestments >= minVal但cumReceivedInvestments >= maxVal的场景,触发一次True&&False分支,完成覆盖后再恢复正常约束。
2. 放松测试环境的Tier约束
如果业务允许,可以在测试环境中给Tier类开后门,允许构造maxVal <= minVal的实例。比如在测试合约中添加一个特殊的构造函数,或者临时注释掉生产环境中require(tiers[i].maxVal() > tiers[i].minVal())的校验逻辑,这样就能直接构造出触发True&&False的测试场景。
3. 合理妥协,忽略该分支覆盖
如果业务逻辑上已经通过前置校验(比如合约构造时的require、部署前的参数校验)确保了所有Tier实例的maxVal > minVal,那么True&&False分支在实际运行中永远不会触发。这种情况下,你可以在LCOV的配置文件中添加规则,忽略这个不可能触发的分支,没必要为了形式上的100%覆盖率构造无意义的测试场景。
4. 调整条件表达式的写法
部分覆盖率工具对短路与的分支识别存在逻辑偏差,你可以尝试把条件写成等效的形式,让工具不再认为存在未覆盖分支:
// 交换条件顺序,或者用区间合并的写法 if (cumReceivedInvestments >= tiers[i].minVal() && cumReceivedInvestments < tiers[i].maxVal()) { return tiers[i]; }
不过这种方式不一定对所有版本的LCOV生效,需要实际测试验证。
内容的提问来源于stack exchange,提问作者a.t.

