Python 突变测试介绍
创始人
2024-03-02 06:07:39
0

通过突变测试来修复未知的 bug。

你一定对所有内容都进行了测试,也许你甚至在项目仓库中有一个徽章,标明有 100% 的测试覆盖率,但是这些测试真的帮到你了吗?你怎么知道的?

开发人员很清楚单元测试的成本。测试必须要编写。有时它们无法按照预期工作:存在假告警或者抖动测试。在不更改任何代码的情况下有时成功,有时失败。通过单元测试发现的小问题很有价值,但是通常它们悄无声息的出现在开发人员的机器上,并且在提交到版本控制之前就已得到修复。但真正令人担忧的问题大多是看不见的。最糟糕的是,丢失的告警是完全不可见的:你看不到没能捕获的错误,直到出现在用户手上 —— 有时甚至连用户都看不到。

有一种测试可以使不可见的错误变为可见: 突变测试 mutation testing 。

变异测试通过算法修改源代码,并检查每次测试是否都有“变异体”存活。任何在单元测试中幸存下来的变异体都是问题:这意味着对代码的修改(可能会引入错误)没有被标准测试套件捕获。

Python 中用于突变测试的一个框架是 mutmut

假设你需要编写代码来计算钟表中时针和分针之间的角度,直到最接近的度数,代码可能会这样写:

def hours_hand(hour, minutes):
    base = (hour % 12 ) * (360 // 12)
    correction = int((minutes / 60) * (360 // 12))
    return base + correction

def minutes_hand(hour, minutes):
    return minutes * (360 // 60)

def between(hour, minutes):
    return abs(hours_hand(hour, minutes) - minutes_hand(hour, minutes))

首先,写一个简单的单元测试:

import angle

def test_twelve():
    assert angle.between(12, 00) == 0

足够了吗?代码没有 if 语句,所以如果你查看覆盖率:

$ coverage run `which pytest`
============================= test session starts ==============================
platform linux -- Python 3.8.3, pytest-5.4.3, py-1.8.2, pluggy-0.13.1
rootdir: /home/moshez/src/mut-mut-test
collected 1 item                                                              

tests/test_angle.py .                                                    [100%]

============================== 1 passed in 0.01s ===============================

完美!测试通过,覆盖率为 100%,你真的是一个测试专家。但是,当你使用突变测试时,覆盖率会变成多少?

$ mutmut run --paths-to-mutate angle.py

Legend for output:
? Killed mutants.   The goal is for everything to end up in this bucket.
⏰ Timeout.          Test suite took 10 times as long as the baseline so were killed.
? Suspicious.       Tests took a long time, but not long enough to be fatal.
? Survived.         This means your tests needs to be expanded.
? Skipped.          Skipped.

⠋ 21/21  ? 5  ⏰ 0  ? 0  ? 16  ? 0

天啊,在 21 个突变体中,有 16 个存活。只有 5 个通过了突变测试,但是,这意味着什么呢?

对于每个突变测试,mutmut 会修改部分源代码,以模拟潜在的错误,修改的一个例子是将 > 比较更改为 >=,查看会发生什么。如果没有针对这个边界条件的单元测试,那么这个突变将会“存活”:这是一个没有任何测试用例能够检测到的潜在错误。

是时候编写更好的单元测试了。很容易检查使用 results 所做的更改:

$ mutmut results

Survived ? (16)

---- angle.py (16) ----

4-7, 9-14, 16-21
$ mutmut apply 4
$ git diff
diff --git a/angle.py b/angle.py
index b5dca41..3939353 100644
--- a/angle.py
+++ b/angle.py
@@ -1,6 +1,6 @@
 def hours_hand(hour, minutes):
     hour = hour % 12
-    base = hour * (360 // 12)
+    base = hour / (360 // 12)
     correction = int((minutes / 60) * (360 // 12))
     return base + correction

这是 mutmut 执行突变的一个典型例子,它会分析源代码并将运算符更改为不同的运算符:减法变加法。在本例中由乘法变为除法。一般来说,单元测试应该在操作符更换时捕获错误。否则,它们将无法有效地测试行为。按照这种逻辑,mutmut 会遍历源代码仔细检查你的测试。

你可以使用 mutmut apply 来应用失败的突变体。事实证明你几乎没有检查过 hour 参数是否被正确使用。修复它:

$ git diff
diff --git a/tests/test_angle.py b/tests/test_angle.py
index f51d43a..1a2e4df 100644
--- a/tests/test_angle.py
+++ b/tests/test_angle.py
@@ -2,3 +2,6 @@ import angle
 
 def test_twelve():
     assert angle.between(12, 00) == 0
+
+def test_three():
+    assert angle.between(3, 00) == 90

以前,你只测试了 12 点钟,现在增加一个 3 点钟的测试就足够了吗?

$ mutmut run --paths-to-mutate angle.py

⠋ 21/21  ? 7  ⏰ 0  ? 0  ? 14  ? 0

这项新测试成功杀死了两个突变体,比以前更好,当然还有很长的路要走。我不会一一解决剩下的 14 个测试用例,因为我认为模式已经很明确了。(你能将它们降低到零吗?)

变异测试和覆盖率一样,是一种工具,它允许你查看测试套件的全面程度。使用它使得测试用例需要改进:那些幸存的突变体中的任何一个都是人类在篡改代码时可能犯的错误,以及潜伏在程序中的隐藏错误。继续测试,愉快地搜寻 bug 吧。


via: https://opensource.com/article/20/7/mutmut-python

作者:Moshe Zadka 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

X平台测试“AI生成”内容...
IT之家 2 月 23 日消息,独立应用程序研究员 Nima Ow...
2026-02-23 15:48:20
AI时代生存与发展的十大原...
《AI赋能》[美]里德·霍夫曼 著 责编| 贾宁排版| 拾零 第 ...
2026-02-13 22:24:51
千问APP测试AI购买电影...
人民财讯2月10日电,2月10日,记者获悉,千问APP正测试接入大...
2026-02-10 13:48:26
一个人×AI,何以让建筑设...
证券时报记者 卓泳 “给我一栋‘像少女般翩翩起舞’的楼。”胡政涛耗...
2026-02-06 06:46:21
原创 ...
作品声明:个人观点、仅供参考 前言 12月4日,美国政府发布...
2026-01-22 18:48:40
我们是否用错误的方式测试A...
当人们想要对人工智能的现状及其意义有一个清醒的认识时,他们往往会求...
2026-01-21 19:47:46

热门资讯

2020 年的 GitHub ... 距离 2020 年结束只剩下区区 24 天,我们即将结束魔幻的 2020 ,迎来新的一年,新的一年或...
开源新闻速递:openSUSE... 今日关注openSUSE 项目组的 Dominique Leuenberger 在他的周报中说:“这...
KDE4.11 Beta1 正...   KDE 项目工作组刚刚发布了 KDE Software Compilation 4.11 Bet...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
使用时间序列数据,用开源工具助... InfluxData 是一个开源的时间序列数据库平台。下面介绍了它是如何被用于边缘应用案例的。收集到...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
Textual:为 Pytho... 快速入门使用 TextualPython 在 Linux 上有像 TkInter 这样的优秀 GUI...
硬核观察 #1245 Open... #1 OpenAI 悄然删除禁止将 ChatGPT 用于 “军事和战争” 的禁令在 1 月 10 日...