Move 的 Mutation Testing:提升单元测试质量

  • aptoslabs
  • 发布于 2025-03-28 23:22
  • 阅读 8

文章介绍了 Move 生态中的 Mutation Testing 工具 Move Mutation Tester,它通过自动修改源码生成多个“突变体”,再运行原有单元测试来判断测试是否能发现这些故障。

作者:Karlo Mardešić (Eiger), Vineeth Kashyap (Aptos Labs)

摘要

对于处理高价值资产的合约来说,穷尽式测试至关重要。但你怎么知道测试是否已经足够好了呢?Move Mutation Tester 是 Move 工具集中的一款新工具,它可以自动发现测试套件中的盲点。

介绍

良好的单元测试对于确保软件正确性和体现预期行为非常重要。事实上,它们是现代软件工程中的常见实践。要以高度信心演进软件,单元测试也必不可少:即在修改代码(添加新行为或修复 bug)时,不会破坏原有的预期行为。在智能合约领域,良好的单元测试甚至更加重要,因为有缺陷的代码可能会带来严重的财务后果。

衡量代码覆盖率是评估单元测试质量的主要方法。Aptos CLI 提供了一个 coverage 工具,我们建议将其作为最佳实践使用。然而,代码覆盖率只能衡量某一行代码是否被单元测试覆盖,却不能衡量单元测试是否对被测代码执行了正确的语义检查。在之前的一篇博客中,我们展示了为什么代码覆盖率不足以衡量单元测试的质量。

Mutation testing 是一种用于发现单元测试盲点的技术,其能力远不止覆盖率工具。它会在源代码中注入故障,并检查单元测试是否能够检测到这些故障。我们现已发布 move-mutation-test 工具的首个版本(可在这里获取,由我们在 Eiger 的合作伙伴开发)。它可以评估 Move 单元测试的质量,我们非常希望你能在自己的 Move 项目中试用它,并向我们反馈。

工具如何工作?

该工具分两步运行:

  1. 它以各种方式修改原始源代码。每个修改后的版本称为一个 mutant
  2. 然后,工具会针对每个 mutant 运行未经修改的单元测试。我们期望这些 mutant 会导致单元测试失败。可能有两种结果:
  • mutant 导致测试失败。这些 mutant 被称为被杀死的 mutant,表明测试捕获到了注入的故障。
  • 测试在某个 mutant 上仍然成功(称为存活 mutant)。这表明测试中可能存在盲点。

如果 mutation test 工具的报告中存在大量存活 mutant,那么就值得考虑改进单元测试。

工具使用示例讲解

你可以使用 aptos update move-mutation-test 安装该工具(请确保使用最新的 aptos CLI,至少为 7.0.0 版本)。另外,你也可以从源码安装

让我们在一个真实项目中做一个小练习,看看该工具可以如何使用。我们将使用 aptos-stdlib 项目。

该工具有两个主要子命令:

  • move-mutation-test run(运行工具并生成报告),
  • move-mutation-test display-report(以更友好的方式展示结果)

该工具可以为整个项目生成大量 mutant,并且对每个 mutant 运行测试可能相对较慢。推荐的使用方式是更有针对性一些,也就是按模块或按函数分别使用。

让我们选择 fixed_point64 模块来生成 mutant。为了避免生成那些由于缺少单元测试覆盖而必然存活的 mutant,我们使用 --coverage 标志,以确保只在具有适当单元测试覆盖的代码部分生成 mutant。

记住我,以便更快登录

注意:要使用 --coverage 标志,用户首先需要运行 aptos move test --coverage 命令,在项目文件中本地生成单元测试 coverage 报告。下面我们假设该命令已经运行过。

move-mutation-test run --coverage --output report.txt --mutate-modules fixed_point64

该命令执行完成后,我们应该会看到一个简短的摘要,告诉我们该模块中每个函数的存活 mutant 数量。

从上面的报告中,我们可以看到,函数 round 有九个存活 mutant。让我们使用以下命令更详细地查看结果:

move-mutation-test display-report coverage --path-to-report report.txt

如果继续向下滚动,就会找到 round 函数,在那里我们可以看到 mutation testing coverage:也就是包含 killed/total mutants 信息的那些行:

请注意,理想情况下是让所有或大多数 mutant 都被杀死。这只是该函数 mutation testing coverage 的一个简单概览,但它并不能告诉我们哪些 mutant 存活了。为此,我们可以使用 mutants 子命令:

move-mutation-test display-report mutants --modules fixed_point64 --functions round

接下来,让我们看看如何改进单元测试,使这些 mutant 能够导致测试失败。

从上面可以看出,round 函数的测试还可以进一步改进。让我们尝试用下面的内容来增强这些测试:

现在,我们可以重新运行该工具。这次让我们更具体一些,以缩短执行时间,并使用以下命令只对 round 函数进行 mutation:

move-mutation-test run --coverage --output report.txt --mutate-modules fixed_point64 --mutate-functions round

我们已经可以从摘要报告中看到,这个函数的统计数据有所改善!

让我们再次使用 display-report coverage 命令检查 coverage:

move-mutation-test display-report coverage

现在,mutation testing 的 coverage 已经提升了(更多 mutant 被杀死),从而提高了测试的质量。

上面的输出类似于单元测试 coverage 报告。它展示了每一行中 killed mutants 数量占总 mutants 数量的统计信息。

结论

Mutation testing 是对代码 coverage 工具的补充,有助于深入了解单元测试的质量。它可以发现单元测试中的盲点,从而也有可能识别源代码中的 bug。我们建议尝试使用它,尤其适用于高保证的 Move 合约。

请注意,就像在所有地方都努力达到 100% 的代码覆盖率可能并不值得(有时甚至会适得其反,因为这会使单元测试过于脆弱)一样,达到 100% 的 mutation testing coverage 也可能让你误入歧途。mutation testing 的结果应作为指导,并结合项目本身的判断,来识别代码库中哪些部分应获得更多关注,以改进测试。

  • 原文链接: medium.com/aptoslabs/mut...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
aptoslabs
aptoslabs
江湖只有他的大名,没有他的介绍。