TranslateProject/translated/tech/20190923 Mutation testing by example- How to leverage failure.md
2019-10-19 00:05:25 +08:00

8.6 KiB
Raw Blame History

变异测试:如何利用故障?

使用事先设计好的故障以确保你的代码达到预期的结果,并遵循 .NET xUnit.net 测试框架来进行测试。 failure sign at a party, celebrating failure

在变异测试是TDD的演变一文中, 我谈到了迭代的力量。在可度量的测试中,迭代能够保证找到问题的解决方案。 在那篇文章中,我们讨论了迭代法帮助确定实现计算给定数字平方根的代码。

我还演示了最有效的方法是找到可衡量的目标或测试,然后以最佳猜测值开始迭代。 正如所预期的,第一次测试通常会失败。因此,必须根据可衡量的目标或测试对失败的代码进行完善。 根据运行结果,对测试值进行验证或进一步加以完善。 在此模型中,学习获得解决方案的唯一方法是反复失败。 这听起来有悖常理,但它确实有效。

按照这种分析,本文探讨了在构建包含某些依赖项的解决方案时使用 DevOps 的最佳方法。 第一步是编写一个预期结果失败的用例。

依赖性问题是你不能依赖它们

正如迈克尔•尼加德Michael Nygard在_Architecture without an end state_,表达的那样,依赖问题是一个很大的话题,最好留到另一篇文章中讨论。 在这里,你将会看到依赖项给项目带来的一些潜在问题,以及 如何利用测试驱动开发TDD来避免这些陷阱。

首先找到现实生活中的一个挑战然后看看如何使用TDD解决它。

谁让猫出来?

一只猫站在屋顶

在敏捷开发环境中,通过定义期望结果开始构建解决方案会很有帮助。 通常,在 用户故事中描述期望结果:

我想使用我家的自动化系统HAS来控制猫何时可以出门因为我想保证它在夜间的安全。

现在你已经有了一个用户故事,你需要通过提供一些功能要求(即指定验收标准)来对其进行详细说明。 从伪代码中描述的最简单的场景开始:

场景1在夜间关闭猫门

  • 用时钟监测到晚上时间
  • 时钟通知 HAS 系统
  • HAS 关闭支持物联网IoT的猫门

分解系统

开始构建之前你需要将正在构建的系统HAS进行分解分解为依赖项。 你必须要做的第一件事是识别任何依赖项(如果幸运的话,你的系统没有依赖项,这将会更容易,但是,这样的系统可以说不是非常有用)。

从上面的简单场景中,你可以看到所需的业务成果(自动控制猫门)取决于对夜间情况监测。 这种依赖性取决于时钟。 但是时钟是无法区分白天和夜晚的。 需要你来提供这种逻辑。

正在构建的系统中的另一个依赖项是能够自动访问猫门并启用或关闭它。 该依赖项很可能取决于具有 IoT 功能的猫门提供的API。

依赖管理面临快速失败

为了满足一个依赖项,我们将构建确定当前时间是白天还是晚上的逻辑。 本着TDD的精神我们将从一个小小的失败开始。

有关如何设置此练习所需的开发环境和脚手架的详细说明,请参阅我的上一篇文章。 我们将重用相同的 NET 环境和 xUnit.net 框架。

接下来,创建一个名为 HAS“家庭自动化系统”的新项目创建一个名为UnitTest1.cs的文件。 在该文件中,编写第一个失败的单元测试。 在此单元测试中,描述你的期望结果。 例如当系统运行时如果时间是晚上7点负责确定是白天还是夜晚的组件将返回值“ Nighttime”。

这是描述期望值的单元测试:

using System;
using Xunit;

namespace unittest
{
   public class UnitTest1
   {
       DayOrNightUtility dayOrNightUtility = [new][7] DayOrNightUtility();

       [Fact]
       public void Given7pmReturnNighttime()
       {
           var expected = "Nighttime";
           var actual = dayOrNightUtility.GetDayOrNight();
           Assert.Equal(expected, actual);
       }
   }
}

至此,你可能已经熟悉了单元测试的结构。 快速复习:在此示例中,通过给单元测试一个描述性名称Given7pmReturnNighttime 来描述期望结果。 然后,在单元测试的主体中,创建一个名为expected 的变量,并为该变量指定期望值(在该示例中,值为“ Nighttime”。 然后,为实际变量指定一个 actual(在组件或服务处理一天中的时间之后可用)。

最后,通过断言期望值和实际值是否相等来检查是否满足期望结果:Assert.Equal(expected, actual)

你还可以在上面的列表中看到名为dayOrNightUtility 的组件或服务。 该模块能够接收消息GetDayOrNight,并且返回string 类型的值。

同样本着TDD的精神描述的组件或服务还尚未构建仅为了后面说明在此进行描述。 构建这些是由所描述的期望结果来驱动的。

app 文件夹中创建一个新文件,并将其命名为DayOrNightUtility.cs。 将以下 C 代码添加到该文件中并保存:

using System;

namespace app {
   public class DayOrNightUtility {
       public string GetDayOrNight() {
           string dayOrNight = "Undetermined";
           return dayOrNight;
       }
   }
}

现在转到命令行,将目录更改为unittests文件夹,然后运行:

[Xunit.net 00:00:02.33] unittest.UnitTest1.Given7pmReturnNighttime [FAIL]
Failed unittest.UnitTest1.Given7pmReturnNighttime
[...]

恭喜,你已经完成了第一个失败的单元测试。 单元测试的期望结果是DayOrNightUtility方法返回字符串“ Nighttime”但相反它返回是“ Undetermined”。

修复失败的单元测试

修复失败的测试的一种快速而粗略的方法是将值“ Undetermined”替换为值“ Nighttime”并保存更改

using System;

namespace app {
   public class DayOrNightUtility {
       public string GetDayOrNight() {
           string dayOrNight = "Nighttime";
           return dayOrNight;
       }
   }
}

现在运行时,成功了。

Starting test execution, please wait...

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 2.6470 Seconds

但是,对值进行硬编码基本上是在作弊,最好为DayOrNightUtility 方法赋予一些智能。 修改GetDayOrNight方法以包括一些时间计算逻辑:

public string GetDayOrNight() {
    string dayOrNight = "Daylight";
    DateTime time = new DateTime();
    if(time.Hour < 7) {
        dayOrNight = "Nighttime";
    }
    return dayOrNight;
}

该方法现在从系统获取当前时间,并与 Hour比较查看其是否小于上午7点。 如果小于,则处理逻辑将 dayOrNight字符串值从“ Daylight”转换为“ Nighttime”。 现在,单元测试通过。

测试驱动解决方案的开始

现在,我们已经开始了基本的单元测试,并为我们的时间依赖项提供了可行的解决方案。 后面还有更多的测试案例需要执行。

在下一篇文章中,我将演示如何对白天时间进行测试以及如何在整个过程中利用故障。


via: https://opensource.com/article/19/9/mutation-testing-example-tdd

作者:Alex Bunardzic 选题:lujun9972 译者:Morisun029 校对:校对者ID

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