学习mutmut-Python变异测试工具

变异测试可让您识别常规测试未涵盖的错误。



你有适合所有场合的测试吗?还是您的项目存储库甚至包含“大约100%测试覆盖率”帮助?但是,在现实生活中,一切都那么简单和可实现吗?







使用单元测试,一切都差不多了:必须编写它们。有时它们无法按预期工作:存在误报或错误测试,如果没有任何代码更改,它们将返回yes和no。您可以在单元测试中找到的小错误很有价值,但是开发人员通常会在提交之前对其进行修复。但是,我们真的很担心那些经常看不见的错误。最糟糕的是,当产品落入用户手中时,他们常常决定为自己起个名字。



变异测试使您能够处理此类隐患。它以预定的方式修改源代码(引入特殊的错误-所谓的“突变体”),并检查这些突变体是否在其他测试中仍然存在。任何在单元测试中幸存下来的突变体都会得出这样的结论,即标准测试找不到包含错误的相应修改的代码段。



在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


代码中没有ifs让我们检查一下这种单元测试在多大程度上涵盖了所有可能的情况:



$ 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%的覆盖率。但是,当我们进行突变测试时会发生什么?







不好了!在这21个中,多达16个突变体幸存下来。为何如此?



对于每个变异测试,需要修改部分源代码以模拟潜在的错误。这种修改的一个示例是将比较运算符“>”更改为“> =”。如果没有针对此边界条件的单元测试,则该突变错误将继续存在:这是潜在的错误,所有常规测试都不会检测到。



好的。全清。我们需要编写更好的单元测试。然后,使用results命令,看看进行了哪些具体更改:



$ mutmut results
<snip>
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


这是mumut的工作方式的一个典型示例:它分析源代码并将某些运算符替换为其他运算符:例如,通过减法加法,或者在这种情况下,通过除法乘法。一般来说,单元测试应该在更改语句时捕获错误;否则,它们将无法有效测试程序的行为。这是进行某些更改时遵循的逻辑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的检查会节省一天吗?







这项新测试成功杀死了两个突变体:比以前更好,但还需要做更多的工作。我现在不打算针对其余14种情况中的每一种编写解决方案,因为无论如何,这个想法很明确(您可以自己杀死所有突变体吗?)



除了衡量覆盖率之外,突变测试还可以让您评估测试的综合程度。这样,您可以改善测试:任何尚存的突变体都是开发人员可能犯的错误,以及产品中的潜在缺陷。所以,我希望你能杀死更多的突变体!






广告



VDSina在Linux和Windows上提供虚拟服务器-选择一种预安装的OS,或者从映像中安装。






All Articles