做端口映射功能时,跑完单元测试发现覆盖率才62%,CI流水线直接红了——这事儿我上周刚踩过坑。
先别慌,看看是哪块漏了
不是所有代码都得100%覆盖。比如你写了个 checkPortAvailability() 方法,调用系统 netstat 或 ss 命令查端口占用,这种跟系统命令强耦合的逻辑,真实环境里可能因权限、路径、输出格式不同而分支跳转——但你本地 mock 不全,就容易漏掉 stderr 非空、返回码为1却没报错这些分支。
举个例子:
function checkPortAvailability(port) {
return execSync(`ss -tuln | grep :${port}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
.trim()
.length > 0
? false // 端口已被占用
: true;
}这段代码看似简单,但实际运行中:execSync 可能抛异常(命令不存在、权限不足)、grep 没匹配到时返回空字符串、ss 输出格式在不同Linux发行版里还不一样……这些路径,光靠 happy path 测试根本扫不到。
快速补漏三招
第一招:抓日志反推。把端口映射服务跑起来,手动触发几个典型场景(比如映射已占用端口、映射非法IP、配置文件缺失),看控制台报什么错、堆栈在哪断的。哪个函数没进 catch?哪个 if 分支压根没走?记下来,针对性补 case。
第二招:用 Istanbul 看热力图。加一句 nyc --reporter=html npm test,打开生成的 coverage/index.html,红色高亮的地方就是裸奔代码。我上次发现 parseConfigFile() 里对 YAML 格式错误的处理逻辑整整7行从没被执行过——因为测试用的全是合法 YAML。
第三招:绕过“不可测”,改写关键逻辑。像上面那个 checkPortAvailability,与其死磕系统命令的各种异常,不如抽成接口:
class PortChecker {
constructor(executor = execSync) {
this.exec = executor;
}
check(port) { /* ... */ }
}测试时传个模拟函数:new PortChecker((cmd) => { throw new Error('command not found'); }),立刻就能覆盖异常流。
别硬刚100%,盯住核心路径
端口映射的核心是:配置加载 → 端口校验 → iptables/nft 规则写入 → 状态反馈。这四步的主干逻辑必须覆盖全,尤其是规则写入失败后的回滚动作——这才是线上出事时救命的代码。至于日志打点、配置字段的默认值填充这些辅助逻辑,覆盖率低点真没关系。
有次我们上线前发现 iptables -t nat -A PREROUTING 执行失败但没 rollback,结果新规则挂了,老规则还留着,流量直接黑洞。后来补了一个只 mock execSync 返回错误码的测试,覆盖率+3%,更重要的是堵住了这个坑。