通过变异测试修复未知bug。您一定已经测试了所有内容,也许您甚至在项目存储库中有一个徽章,表明您拥有100%的测试覆盖率,但这些测试真的对您有帮助吗?你怎么知道?开发人员很清楚单元测试的成本。必须编写测试。有时它们不会按预期工作:存在误报或抖动测试。有时它会成功,有时它会在不更改任何代码的情况下失败。通过单元测试发现的小错误很有价值,但它们通常会悄无声息地出现在开发人员的机器上,并在提交到版本控制之前得到修复。但真正令人担忧的问题大多是看不见的。最糟糕的是,丢失的警报是完全不可见的:在到达用户之前您看不到未被捕获的错误——有时甚至用户都看不到。有一种测试可以使不可见的错误可见:突变测试。突变测试通过算法修改源代码并检查是否有任何“突变体”在每次测试中幸存下来。任何在单元测试中幸存下来的变体都是一个问题:这意味着对代码的修改(可能引入错误)不会被标准测试套件捕获。Python中的一种突变测试框架是mutmut。假设您需要编写代码来计算时钟的时针和分针之间的角度,精??确到度数,代码可能如下所示:defhours_hand(hour,minutes):base=(hour%12)*(360//12)correction=int((360//12))returnbase+correctiondefminutes_hand(hour,minutes):returnminutes*(360//60)defbetween(hour,minutes):returnabs(hours_hand(hour,minutes)-minutes_hand(hour,minutes))首先写一个简单的单元测试:importangledeftest_twelve():assertangle.between(12,00)==0是足够了?该代码没有if语句,因此如果您查看覆盖范围:$coveragerun`whichpytest`================================测试会话开始====================================平台linux--Python3.8.3,pytest-5.4.3,py-1.8。2、pluggy-0.13.1rootdir:/home/moshez/src/mut-mut-testcollected1itemtests/test_angle.py。[100%]================================1在0.01秒内通过=================================完美!测试通过,覆盖率100%,不愧是测试高手。但是,当您使用突变测试时,覆盖范围会变成什么样?$mutmutrun--paths-to-mutateangle.py输出图例:🎉杀死变种人。目标是让所有东西都进入这个桶。?超时。测试套件花费的时间是基线的10倍,因此被杀死。🤔可疑的。测试花了很长时间,但还不足以致命。🙁幸存下来。这意味着您的测试需要扩展。🔇跳过。已跳过。<片段>?21/21🎉5?0🤔0🙁16🔇0天啊,21个变种人,16个活下来了。只有5人通过了变异测试,那是什么意思?对于每个变异测试,mutmut修改部分源代码以模拟潜在的错误,修改的一个例子是将>比较更改为>=并查看会发生什么。如果没有针对此边界条件的单元测试,突变将“幸存”:这是一个潜在的错误,任何测试用例都无法检测到。是时候编写更好的单元测试了。使用结果可以很容易地检查所做的更改:$mutmutresultsSurvived🙁(16)----angle.py(16)----4-7、9-14、16-21$mutmutapply4$gitdiffdiff--gita/angle.pyb/angle.pyindexb5dca41..3939353100644---a/angle.py+++b/angle.py@@-1,6+1,6@@defhours_hand(hour,minutes):hour=hour%12-base=hour*(360//12)+base=hour/(360//12)correction=int((minutes/60)*(360//12))returnbase+correction这是执行突变的经典示例由mutmut开发,它分析源代码并将运算符更改为不同的运算符:减法变为加法。在这个例子中,乘法变成了除法。通常,单元测试应该捕获运算符替换的错误。否则,他们无法有效地测试行为。按照这个逻辑,mutmut遍历源代码以仔细检查您的测试。您可以使用mutmutapply来应用失败的突变体。事实证明您几乎没有检查过hour参数是否被正确使用。修复它:$gitdiff--gita/tests/test_angle.pyb/tests/test_angle.pyindexf51d43a..1a2e4df100644---a/tests/test_angle.py+++b/tests/test_angle.py@@-2,3+2,6@@importangledeftest_twelve():assertangle.between(12,00)==0++deftest_three():+assertangle.between(3,00)==90之前,你只12点考了,现在加个3点考够了?$mutmutrun--paths-to-mutateangle.py?21/21🎉7?0🤔0🙁14🔇0这项新测试成功杀死了两个变种人,比以往任何时候都好,当然还有很长的路要走。剩下的14个测试用例我就不一一说了,因为我觉得模式已经很清楚了。(你能把它们降到零吗?)突变测试,就像覆盖率一样,是一种让你看到你的测试套件有多全面的工具。使用它使得测试用例需要改进:任何那些幸存的突变体都是人类在篡改代码时可能犯的错误,以及潜伏在程序中的隐藏错误。继续测试并愉快地寻找错误。