tdd简单示例_通过示例进行突变测试:从脆弱的TDD演变而来

tdd简单示例

本系列的第三篇文章演示了如何使用故障和单元测试来开发更好的代码。

虽然它似乎是旅程结束了与事(物联网)应用程序来控制猫门的一个成功样本的互联网,有经验的程序员都知道的解决方案需要的突变

什么是突变测试?

变异测试是以下过程:遍历已实施代码的每一行,变异该行,然后运行单元测试,并检查变异是否超出预期。 如果还没有,则说明您创建了一个尚存的突变体。

如果您让幸存的突变者四处奔波,繁衍,长寿和繁荣,那么您将制造出可怕的技术债务。 另一方面,如果任何单元测试抱怨该临时突变的代码行产生的输出与预期输出不同,则该突变体已被杀死。

安装史赛克

尝试突变测试的最快方法是利用专用框架。 本示例使用Stryker

要安装Stryker,请转到命令行并运行:

 $  dotnet tool install -g dotnet-stryker 

要运行Stryker,请导航至unittest文件夹并键入:

 $  dotnet-stryker 

以下是史赛克关于我们解决方案质量的报告:


   
   
14 mutants have been created. Each mutant will now be tested, this could take a while.

Tests progress | 14/14 | 100% | ~0m 00s |
Killed : 13
Survived : 1
Timeout : 0

All mutants have been tested, and your mutation score has been calculated
- \app [13/14 (92.86%)]
[...]

该报告说:

  • 斯特赖克创造了14个突变体
  • Stryker看到13个突变体被单元测试杀死
  • 斯特赖克(Stryker)看到一个突变体幸免于单元测试的冲击
  • Stryker计算得出,现有代码库包含满足期望的代码的92.86%
  • Stryker计算得出,7.14%的代码库包含的代码不符合预期

总体而言,Stryker声称本系列前三篇文章中组装的应用程序无法产生可靠的解决方案。

如何杀死一个突变体

当软件开发人员遇到幸存的突变体时,他们通常会伸手寻找实现的代码并寻找修改它的方法。 例如,对于用于猫门自动化的示例应用程序,请更改以下行:

 string trapDoorStatus = "Undetermined" ; 

至:

 string trapDoorStatus = "" ; 

然后再次运行Stryker。 突变体幸存下来:


   
   
All mutants have been tested, and your mutation score has been calculated
- \app [13/14 (92.86%)]
[...]
[Survived] String mutation on line 4: '""' ==> '"Stryker was here!"'
[...]

这次,您可以看到Stryker改变了这一行:

 string trapDoorStatus = "" ; 

变成:

 string trapDoorStatus = "" Stryker was here ! "; 

这是Stryker工作原理的一个很好的例子:它以一种聪明的方式改变了我们代码的每一行,以查看是否还有我们尚未考虑的测试用例。 这迫使我们更深入地考虑我们的期望。

在Stryker的击败下,您可以尝试通过向其添加更多逻辑来改进已实现的代码:


   
   
public string Control ( string dayOrNight ) {
    string trapDoorStatus = "Undetermined" ;
    if ( dayOrNight == "Nighttime" ) {
       trapDoorStatus = "Cat trap door disabled" ;
    } else if ( dayOrNight == "Daylight" ) {
       trapDoorStatus = "Cat trap door enabled" ;
    } else {
       trapDoorStatus = "Undetermined" ;
    }
    return trapDoorStatus ;
}

但是在再次运行Stryker之后,您会看到此尝试创建了一个新的变体:


   
   
ll mutants have been tested, and your mutation score has been calculated
- \app [13/15 (86.67%)]
[...]
[Survived] String mutation on line 4: '"Undetermined"' ==> '""'
[...]
[Survived] String mutation on line 10: '"Undetermined"' ==> '""'
[...]
Stryker report

您不能通过修改实现的代码来摆脱困境。 事实证明,杀死存活的突变体的唯一方法是描述其他期望 。 您如何描述期望? 通过编写单元测试。

成功进行单元测试

是时候添加一个新的单元测试了。 由于尚存的突变体位于第4行,因此您意识到您没有为输出指定期望值“ Undetermined”。

让我们添加一个新的单元测试:


   
   
[ Fact ]
public void GivenIncorrectTimeOfDayReturnUndetermined ( ) {
    var expected = "Undetermined" ;
    var actual = catTrapDoor . Control ( "Incorrect input" ) ;
   Assert . Equal ( expected, actual ) ;
}

解决方法成功了! 现在所有的突变体都被杀死了:


   
   
All mutants have been tested, and your mutation score has been calculated
- \app [14/14 (100%)]
[Killed] [...]

您最终将获得一个完整的解决方案,包括对如果系统接收到错误输入值的预期输出的描述。

变异测试以营救

假设您决定过度设计解决方案,并将此方法添加到FakeCatTrapDoor


   
   
private string getTrapDoorStatus ( string dayOrNight ) {
    string status = "Everything okay" ;
    if ( dayOrNight != "Nighttime" || dayOrNight != "Daylight" ) {
       status = "Undetermined" ;
    }
    return status ;
}

然后替换第4行的语句:

 string trapDoorStatus = "Undetermined" ; 

与:

 string trapDoorStatus = getTrapDoorStatus ( dayOrNight ) ; 

运行单元测试时,一切都会通过:


   
   
Starting test execution, please wait...

Total tests: 5. Passed: 5. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 2.7191 Seconds

测试已顺利通过。 TDD已奏效。 但是将Stryker带到现场,突然的画面看起来有些冷酷:


   
   
All mutants have been tested, and your mutation score has been calculated
- \app [14/20 (70%)]
[...]

斯特赖克创造了20个突变体; 14个突变体被杀死,六个突变体幸存。 这会将成功分数降低到70%。 这意味着只有70%的代码可以满足所描述的期望。 其余30%的代码没有明确的原因,这使我们有滥用该代码的风险。

在这种情况下,Stryker有助于对抗膨胀。 它不鼓励使用不必要的和复杂的逻辑,因为它位于这样的不必要的复杂逻辑的缝隙中,在这些逻辑中错误和缺陷会滋生。

结论

如您所见,变异测试确保没有不确定的事实得到检验。

您可以将Stryker与国际象棋大师进行比较,后者可以考虑赢得一场比赛的所有可能动作。 当Stryker不确定时,它告诉您获胜还不是保证。 我们记录为事实的单元测试越多,我们在比赛中的距离就越远,Stryker预测获胜的可能性就越大。 无论如何,即使表面上看起来一切都很好,Stryker仍可以帮助您发现失败的情况。

适当地设计代码始终是一个好主意。 您已经了解了TDD在这方面如何提供帮助。 当要保持代码的高度模块化时,TDD尤其有用。 但是,单靠TDD不足以提供完全符合预期的精益代码。 开发人员可以在不首先描述期望的情况下将代码添加到已经实现的代码库中。 这使整个代码库面临风险。 突变测试在捕获常规测试驱动开发(TDD)节奏中的违规行为时特别有用。 您需要更改已实现代码的每一行,以确保没有特定原因的情况下没有代码行。

既然您了解了变异测试的工作原理,那么您应该研究如何利用它。 下次,我将向您展示在应对更复杂的场景时如何充分利用突变测试。 我还将介绍更多敏捷概念,以了解DevOps文化如何从成熟的技术中受益。

翻译自: https://opensource.com/article/19/9/mutation-testing-example-definition

tdd简单示例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
测试驱动的编程是 XP 困扰程序员的一个方面。对于测试驱动的编程意味着什么以及如何去做,大多数人都做出了不正确的假设。这个月,XP 方面的讲师兼 Java 开发人员 Roy Miller 谈论了测试驱动的编程是什么,它为什么可以使程序员的生产力和质量发生巨大变化,以及编写测试的原理。请在与本文相随的 论坛中提出您就本文的想法,以飨笔者和其他读者。(您也可以单击本文顶部或底部的“讨论”来访问该论坛。) 最近 50 年来,测试一直被视为项目结束时要做的事。当然,可以在项目进行之中结合测试测试通常并不是在 所有编码工作结束后才开始,而是一般在稍后阶段进行测试。然而,XP 的提倡者建议完全逆转这个模型。作为一名程序员,应该在编写代码 之前编写测试,然后只编写足以让测试通过的代码即可。这样做将有助于使您的系统尽可能的简单。 先编写测试 XP 涉及两种测试: 程序员测试和 客户测试测试驱动的编程(也称为 测试为先编程)最常指第一种测试,至少我使用这个术语时是这样。测试驱动的编程是让 程序员测试(即单元测试 ― 重申一下,只是换用一个术语)决定您所编写的代码。这意味着您必须在编写代码之前进行测试测试指出您 需要编写的代码,从而也 决定了您要编写的代码。您只需编写足够通过测试的代码即可 ― 不用多,也不用少。XP 规则很简单:如果不进行程序员测试,则您不知道要编写什么代码,所以您不会去编写任何代码。 测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。 背景 一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从。最近兴起的一些软件开发过程相关的技术,提供一些比较高效、实用的软件过程开发方法。其中比较基础、关键的一个技术就是测试驱动开发(Test-Driven Development)。虽然TDD光大于极限编程,但测试驱动开发完全可以单独应用。下面就从开发人员使用的角度进行介绍,使开发人员用最少的代价尽快理解、掌握、应用这种技术。下面分优势,原理,过程,原则,测试技术,Tips等方面进行讨论。 1. 优势 TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值