translated

This commit is contained in:
Morisun029 2019-10-16 00:09:10 +08:00 committed by GitHub
parent 9d7086d878
commit e98f501842
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 305 additions and 285 deletions

View File

@ -1,285 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (Morisun029)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Mutation testing is the evolution of TDD)
[#]: via: (https://opensource.com/article/19/8/mutation-testing-evolution-tdd)
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
Mutation testing is the evolution of TDD
======
Since test-driven development is modeled on how nature works, mutation
testing is the natural next step in the evolution of DevOps.
![Ants and a leaf making the word "open"][1]
In "[Failure is a feature in blameless DevOps][2]," I discussed the central role of failure in delivering quality by soliciting feedback. This is the failure agile DevOps teams rely on to guide them and drive development. [Test-driven development (TDD)][3] is the _[conditio sine qua non][4]_ of any agile DevOps value stream delivery. Failure-centric TDD methodology only works if it is paired with measurable tests.
TDD methodology is modeled on how nature works and how nature produces winners and losers in the evolutionary game.
### Natural selection
![Charles Darwin][5]
In 1859, [Charles Darwin][6] proposed the theory of evolution in his book _[On the Origin of Species][7]_. Darwin's thesis was that natural variability is caused by the combination of spontaneous mutations in individual organisms and environmental pressures. These pressures eliminate less-adapted organisms while favoring other, more fit organisms. Each and every living being mutates its chromosomes, and those spontaneous mutations are carried to the next generation (the offspring). The newly emerged variability is then tested under natural selection—the environmental pressures that exist due to the variability of environmental conditions.
This simplified diagram illustrates the process of adjusting to environmental conditions.
![Environmental pressures on fish][8]
Fig. 1. Different environmental pressures result in different outcomes governed by natural selection. Image screenshot from a [video by Richard Dawkins][9].
This illustration shows a school of fish in their natural habitat. The habitat varies (darker or lighter gravel at the bottom of the sea or riverbed), as does each fish (darker or lighter body patterns and colors).
It also shows two situations (i.e., two variations of the environmental pressure):
1. The predator is present
2. The predator is absent
In the first situation, fish that are easier to spot against the gravel shade are at higher risk of being picked off by predators. When the gravel is darker, the lighter portion of the fish population is thinned out. And vice versa—when the gravel is a lighter shade, the darker portion of the fish population suffers the thinning out scenario.
In the second situation, fish are sufficiently relaxed to engage in mating. In the absence of predators and in the presence of the mating ritual, the opposite results can be expected: The fish that stand out against the background have a better chance of being picked for mating and transferring their characteristics to offspring.
### Selection criteria
When selecting among variability, the process is never arbitrary, capricious, whimsical, nor random. The decisive factor is always measurable. That decisive factor is usually called a _test_ or a _goal_.
A simple mathematical example can illustrate this process of decision making. (Only in this case it won't be governed by natural selection, but by artificial selection.) Suppose someone asks you to build a little function that will take a positive number and calculate that number's square root. How would you go about doing that?
The agile DevOps way is to _fail fast_. Start with humility, admitting upfront that you don't really know how to develop that function. All you know, at this point, is how to _describe_ what you'd like to do. In technical parlance, you are ready to engage in crafting a _unit test_.
"Unit test" describes your specific expectation. It could simply be formulated as "given the number 16, I expect the square root function to return number 4." You probably know that the square root of 16 is 4. However, you don't know the square root for some larger numbers (such as 533).
At the very least, you have formulated your selection criteria, your test or goal.
### Implement the failing test
The [.NET Core][10] platform can illustrate the implementation. .NET typically uses [xUnit.net][11] as a unit-testing framework. (To follow the coding examples, please install .NET Core and xUnit.net.)
Open the command line and create a folder where your square root solution will be implemented. For example, type:
```
`mkdir square_root`
```
Then type:
```
`cd square_root`
```
Create a separate folder for unit tests:
```
`mkdir unit_tests`
```
Move into the **unit_tests** folder (**cd unit_tests**) and initiate the xUnit framework:
```
`dotnet new xunit`
```
Now, move one folder up to the **square_root** folder, and create the **app** folder:
```
mkdir app
cd app
```
Create the scaffold necessary for the C# code:
```
`dotnet new classlib`
```
Now open your favorite editor and start cracking!
In your code editor, navigate to the **unit_tests** folder and open **UnitTest1.cs**.
Replace auto-generated code in **UnitTest1.cs** with:
```
using System;
using Xunit;
using app;
namespace unit_tests{
   public class UnitTest1{
       Calculator calculator = new Calculator();
       [Fact]
       public void GivenPositiveNumberCalculateSquareRoot(){
           var expected = 4;
           var actual = calculator.CalculateSquareRoot(16);
           Assert.Equal(expected, actual);
       }
   }
}
```
This unit test describes the expectation that the variable **expected** should be 4. The next line describes the **actual** value. It proposes to calculate the **actual** value by sending a message to the component called **calculator**. This component is described as capable of handling the **CalculateSquareRoot** message by accepting a numeric value. That component hasn't been developed yet. But it doesn't really matter, because this merely describes the expectations.
Finally, it describes what happens when the message is triggered to be sent. At that point, it asserts whether the **expected** value is equal to the **actual** value. If it is, the test passed and the goal is reached. If the **expected** value isn't equal to the **actual value**, the test fails.
Next, to implement the component called **calculator**, create a new file in the **app** folder and call it **Calculator.cs**. To implement a function that calculates the square root of a number, add the following code to this new file:
```
namespace app {
   public class Calculator {
       public double CalculateSquareRoot(double number) {
           double bestGuess = number;
           return bestGuess;
       }
   }
}
```
Before you can test this implementation, you need to instruct the unit test how to find this new component (**Calculator**). Navigate to the **unit_tests** folder and open the **unit_tests.csproj** file. Add the following line in the **<ItemGroup>** code block:
```
`<ProjectReference Include="../app/app.csproj" />`
```
Save the **unit_test.csproj** file. Now you are ready for your first test run.
Go to the command line and **cd** into the **unit_tests** folder. Run the following command:
```
`dotnet test`
```
Running the unit test will produce the following output:
![xUnit output after the unit test run fails][12]
Fig. 2. xUnit output after the unit test run fails.
As you can see, the unit test failed. It expected that sending number 16 to the **calculator** component would result in the number 4 as the output, but the output (the **actual** value) was the number 16.
Congratulations! You have created your first failure. Your unit test provided strong, immediate feedback urging you to fix the failure.
### Fix the failure
To fix the failure, you must improve **bestGuess**. Right now, **bestGuess** merely takes the number the function receives and returns it. Not good enough.
But how do you figure out a way to calculate the square root value? I have an idea—how about looking at how Mother Nature solves problems.
### Emulate Mother Nature by iterating
It is extremely hard (pretty much impossible) to guess the correct value from the first (and only) attempt. You must allow for several attempts at guessing to increase your chances of solving the problem. And one way to allow for multiple attempts is to _iterate_.
To iterate, store the **bestGuess** value in the **previousGuess** variable, transform the **bestGuess** value, and compare the difference between the two values. If the difference is 0, you solved the problem. Otherwise, keep iterating.
Here is the body of the function that produces the correct value for the square root of any positive number:
```
double bestGuess = number;
double previousGuess;
do {
   previousGuess = bestGuess;
   bestGuess = (previousGuess + (number/previousGuess))/2;
} while((bestGuess - previousGuess) != 0);
return bestGuess;
```
This loop (iteration) converges bestGuess values to the desired solution. Now your carefully crafted unit test passes!
![Unit test successful][13]
Fig. 3. Unit test successful, 0 tests failed.
### The iteration solves the problem
Just like Mother Nature's approach, in this exercise, iteration solves the problem. An incremental approach combined with stepwise refinement is the guaranteed way to arrive at a satisfactory solution. The decisive factor in this game is having a measurable goal and test. Once you have that, you can keep iterating until you hit the mark.
### Now the punchline!
OK, this was an amusing experiment, but the more interesting discovery comes from playing with this newly minted solution. Until now, your starting **bestGuess** was always equal to the number the function receives as the input parameter. What happens if you change the initial **bestGuess**?
To test that, you can run a few scenarios. First, observe the stepwise refinement as the iteration loops through a series of guesses as it tries to calculate the square root of 25:
![Code iterating for the square root of 25][14]
Fig. 4. Iterating to calculate the square root of 25.
Starting with 25 as the **bestGuess**, it takes eight iterations for the function to calculate the square root of 25. But what would happen if you made a comical, ridiculously wrong stab at the **bestGuess**? What if you started with a clueless second guess, that 1 million might be the square root of 25? What would happen in such an obviously erroneous situation? Would your function be able to deal with such idiocy?
Take a look at the horse's mouth. Rerun the scenario, this time starting from 1 million as the **bestGuess**:
![Stepwise refinement][15]
Fig. 5. Stepwise refinement when calculating the square root of 25 by starting with 1 million as the initial **bestGuess**.
Oh wow! Starting with a ludicrously large number, the number of iterations only tripled (from eight iterations to 23). Not nearly as dramatic an increase as you might intuitively expect.
### The moral of the story
The _Aha!_ moment arrives when you realize that, not only is iteration guaranteed to solve the problem, but it doesn't matter whether your search for the solution begins with a good or a terribly botched initial guess. However erroneous your initial understanding, the process of iteration, coupled with a measurable test/goal, puts you on the right track and delivers the solution. Guaranteed.
Figures 4 and 5 show a steep and dramatic burndown. From a wildly incorrect starting point, the iteration quickly burns down to an absolutely correct solution.
This amazing methodology, in a nutshell, is the essence of agile DevOps.
### Back to some high-level observations
Agile DevOps practice stems from the recognition that we live in a world that is fundamentally based on uncertainty, ambiguity, incompleteness, and a healthy dose of confusion. From the scientific/philosophical point of view, these traits are well documented and supported by [Heisenberg's Uncertainty Principle][16] (covering the uncertainty part), [Wittgenstein's Tractatus Logico-Philosophicus][17] (the ambiguity part), [Gödel's incompleteness theorems][18] (the incompleteness aspect), and the [Second Law of Thermodynamics][19] (the confusion caused by relentless entropy).
In a nutshell, no matter how hard you try, you can never get complete information when trying to solve any problem. It is, therefore, more profitable to abandon an arrogant stance and adopt a more humble approach to solving problems. Humility pays big dividends in rewarding you—not only with the hoped-for solution but also with the byproduct of a well-structured solution.
### Conclusion
Nature works incessantly—it's a continuous flow. Nature has no master plan; everything happens as a response to what happened earlier. The feedback loops are very tight, and apparent progress/regress is piecemeal. Everywhere you look in nature, you see stepwise refinement, in one shape or form or another.
Agile DevOps is a very interesting outcome of the engineering model's gradual maturation. DevOps is based on the recognition that the information you have available is always incomplete, so you'd better proceed cautiously. Obtain a measurable test (e.g., a hypothesis, a measurable expectation), make a humble attempt at satisfying it, most likely fail, then collect the feedback, fix the failure, and continue. There is no plan other than agreeing that, with each step of the way, there must be a measurable hypothesis/test.
In the next article in this series, I'll take a closer look at how mutation testing provides much-needed feedback that drives value.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/8/mutation-testing-evolution-tdd
作者:[Alex Bunardzic][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/alex-bunardzic
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_520X292_openanttrail-2.png?itok=xhD3WmUd (Ants and a leaf making the word "open")
[2]: https://opensource.com/article/19/7/failure-feature-blameless-devops
[3]: https://en.wikipedia.org/wiki/Test-driven_development
[4]: https://www.merriam-webster.com/dictionary/conditio%20sine%20qua%20non
[5]: https://opensource.com/sites/default/files/uploads/darwin.png (Charles Darwin)
[6]: https://en.wikipedia.org/wiki/Charles_Darwin
[7]: https://en.wikipedia.org/wiki/On_the_Origin_of_Species
[8]: https://opensource.com/sites/default/files/uploads/environmentalconditions2.png (Environmental pressures on fish)
[9]: https://www.youtube.com/watch?v=MgK5Rf7qFaU
[10]: https://dotnet.microsoft.com/
[11]: https://xunit.net/
[12]: https://opensource.com/sites/default/files/uploads/xunit-output.png (xUnit output after the unit test run fails)
[13]: https://opensource.com/sites/default/files/uploads/unit-test-success.png (Unit test successful)
[14]: https://opensource.com/sites/default/files/uploads/iterating-square-root.png (Code iterating for the square root of 25)
[15]: https://opensource.com/sites/default/files/uploads/bestguess.png (Stepwise refinement)
[16]: https://en.wikipedia.org/wiki/Uncertainty_principle
[17]: https://en.wikipedia.org/wiki/Tractatus_Logico-Philosophicus
[18]: https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems
[19]: https://en.wikipedia.org/wiki/Second_law_of_thermodynamics

View File

@ -0,0 +1,305 @@
[#]: collector: (lujun9972)
[#]: translator: (Morisun029)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Mutation testing is the evolution of TDD)
[#]: via: (https://opensource.com/article/19/8/mutation-testing-evolution-tdd)
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
变异测试是 TDD 的演变
======
测试驱动开发技术是根据大自然的运作规律创建的,变异测试自然成为 DevOps 发展的下一步。
![Ants and a leaf making the word "open"][1]
在 "[故障是无懈可击的开发运维中的一个特点][2]," 我讨论了故障以征求反馈的机制在交付优质产品过程中所起到的重要作用。 敏捷DevOps团队就是用故障来指导他们并推动开发进程的。 [测试驱动开发(TDD)][3] 是任何敏捷 DevOps 团队评估产品交付的[必要条件][4]。 以故障为中心的 TDD 方法仅在与可量化的测试配合使用时才有效。
TDD 方法仿照大自然是如何运作的以及自然界在进化博弈中是如何产生赢家和输家为模型而建立的。
### 自然选择
![查尔斯·达尔文][5]
1859年, [查尔斯·达尔文][6] 在他的[物种起源][7]_一书中提出了进化论学说。 达尔文的论点是,自然变异是由生物个体的自发突变和环境压力共同造成的。 环境压力淘汰了适应性较差的生物体,而有利于其他适应性强的生物的发展。 每个生物体的染色体都会发生变异,而这些自发的变异会携带给下一代(后代)。 然后在自然选择下测试新出现的变异性-当下存在的环境压力是由变异性的环境条件所导致的。
这张简图说明了调整适应环境条件的过程。
![环境压力对鱼类的影响][8]
图1. 不同的环境压力导致自然选择下的不同结果。图片截图来源于[理查德•道金斯的一个视频][9]。
该图显示了一群生活在自己栖息地的鱼。 栖息地各不相同(海底或河床底部的砾石颜色有深有浅),每条鱼长的也各不相同(鱼身图案和颜色也有深有浅)。
这张图还显示了两种情况(即环境压力的两种变化)::
1. 捕食者在场
2. 捕食者不在场
在第一种情况下,在砾石颜色衬托下容易凸显出来的鱼被捕食者捕获的风险更高。 当砾石颜色较深时,浅色鱼的数量会更少一些。 反之亦然-当砾石颜色较浅时,深色鱼的数量会更少。
在第二种情况下,鱼完全放松下来进行交配。 在没有捕食者和没有交配仪式的情况下,可以预料到相反的结果:在砾石背景下显眼的鱼会有更大的机会被选来交配并将其特性传递给后代。
### 选择标准
变异性在进行选择时,绝不是任意的,反复无常的,异想天开的或随机的。选择过程中的决定性因素通常是可以度量的。 该决定性因素通常称为测试或目标。
一个简单的数学例子可以说明这一决策过程。 (在该示例中,这种选择不是由自然选择决定的,而是由人为选择决定。)假设有人要求您构建一个小函数,该函数将选用一个正数,然后计算该数的平方根。你将怎么做?
敏捷 DevOps 团队的方法是快速验证失败。 谦虚一点,承认自己真的不知道如何开发该功能。 这时,你所知道的就是如何描述你想做的事情。 从技术上讲,你已准备好进行单元测试。
“单元测试”描述了你的具体期望结果是什么。 它可以简单地表述为“给定数字16我希望平方根函数返回数字4”。 您可能知道16的平方根是4。但是你不知道一些较大数字例如533的平方根。
但至少,你已经制定了选择标准,即你的测试或你的期望值。
### 进行故障测试
[.NET Core][10] 平台可以实现该测试。.NET 通常使用 xUnit.net 作为单元测试框架。(要遵循编码示例,请安装 .NET Core 和 xUnit.net。
打开命令行并创建一个文件夹,在该文件夹实现平方根解决方案。 例如,输入:
```
`mkdir square_root`
```
再输入:
```
`cd square_root`
```
为单元测试创建一个单独的文件夹:
```
`mkdir unit_tests`
```
进入 **unit_tests** 文件夹下(**cd unit_tests**) 初始化xUnit 框架:
```
`dotnet new xunit`
```
现在,将文件夹移动到 **square_root** 下, 创建 **app** 文件夹:
```
mkdir app
cd app
```
如果有必要的话,为你的代码创建一个脚手架:
```
`dotnet new classlib`
```
现在打开你最喜欢的编辑器开始编码!
在你的代码编辑器中,导航到 **unit_tests** 文件夹,打开 **UnitTest1.cs**
**UnitTest1.cs** 中自动生成的代码替换为:
```
using System;
using Xunit;
using app;
namespace unit_tests{
public class UnitTest1{
Calculator calculator = new Calculator();
[Fact]
public void GivenPositiveNumberCalculateSquareRoot(){
var expected = 4;
var actual = calculator.CalculateSquareRoot(16);
Assert.Equal(expected, actual);
}
}
}
```
该单元测试描述了变量的**期望值**应该为4。下一行描述了**实际值**。 建议通过将输入值发送到称为**calculator** 的组件来计算**实际值**。对该组件的描述是通过接收数值来处理**CalculateSquareRoot**信息。 该组件尚未开发。 但这并不重要,我们在此只是描述期望值。
最后,描述了触发消息发送时发生的情况。 此时,判断**期望值** 是否等于**实际值**。 如果是,则测试通过,目标达成。 如果**期望值** 不等于**实际值**,则测试失败。
接下来,要实现称为**calculator**的组件,在 **app** 文件夹中创建一个新文件,并将其命名为**Calculator.cs**。 要实现计算平方根的功能,请在此新文件中添加以下代码:
```
namespace app {
public class Calculator {
public double CalculateSquareRoot(double number) {
double bestGuess = number;
return bestGuess;
}
}
}
```
Before you can test this implementation, you need to instruct the unit test how to find this new component (**Calculator**). Navigate to the **unit_tests** folder and open the **unit_tests.csproj** file. Add the following line in the **&lt;ItemGroup&gt;** code block:
在测试之前,你需要通知单元测试如何找到该新组件(**Calculator**)。 导航至**unit_tests** 文件夹,打开**unit_tests.csproj**文件。 在 **&lt;ItemGroup&gt;** 代码块中添加以下代码:
```
`<ProjectReference Include="../app/app.csproj" />`
```
保存 **unit_test.csproj** 文件。现在,你可以运行第一个测试了。
切换到命令行,进入 **unit_tests** 文件夹。 运行以下命令:
```
`dotnet test`
```
运行单元测试,会输出以下内容:
![单元测试失败后xUnit的输出结果][12]
图2. 单元测试失败后xUnit的输出结果
正如你所看到的,单元测试失败了。 期望将数字16发送到**calculator** 组件后会输出数字4但是输出**实际值**的是16。
恭喜你! 创建了第一个故障。 单元测试为你提供了强有力的反馈机制,敦促你修复故障。
### 修复故障
要修复故障,你必须要改进 **bestGuess**。 当下,**bestGuess** 仅获取函数接收的数字并返回。 这不够好。
但是,如何找到一种计算平方根值的方法呢? 我有一个主意-看一下大自然母亲是如何解决问题的。
### 效仿大自然的迭代
在第一次(也是唯一的)尝试中要得出正确值是非常难的(几乎不可能)。 你必须允许自己进行多次尝试猜测,以增加解决问题的机会。 允许多次尝试的一种方法是进行迭代。
要迭代,就要将 **bestGuess**值存储在 **previousGuess** 变量中,转换**bestGuess**的值,然后比较两个值之间的差。 如果差为0则说明问题已解决。 否则,继续迭代。
这是生成任何正数的平方根的函数体:
```
double bestGuess = number;
double previousGuess;
do {
previousGuess = bestGuess;
bestGuess = (previousGuess + (number/previousGuess))/2;
} while((bestGuess - previousGuess) != 0);
return bestGuess;
```
该循环迭代将bestGuess值集中到设想的解决方案。 现在,你精心设计的单元测试通过了!
![单元测试通过了][13]
图 3. 单元测试通过了。
### 迭代解决了问题
正如大自然母亲解决问题的方法,在本练习中,迭代解决了问题。 增量方法与逐步改进相结合是获得满意解决方案的有效方法。 该示例中的决定性因素是具有可衡量的目标和测试。 一旦有了这些,就可以继续迭代直到达到目标。
### 关键点!
好的,这是一个有趣的试验,但是更有趣的发现来自于使用这种新创建的解决方案。 到目前为止,**bestGuess** 从开始一直把函数接收到的数字作为输入参数。 如果更改**bestGuess**的初始值会怎样?
为了测试这一点,你可以测试几种情况。 首先在迭代多次尝试计算25的平方根时要逐步细化观察结果
![25平方根的迭代编码][14]
图 4. 通过迭代来计算25的平方根。
以25作为 **bestGuess** 的初始值该函数需要八次迭代才能计算出25的平方根。但是如果在设计 **bestGuess** 初始值上犯下荒谬的错误,那将怎么办? 尝试第二次那100万可能是25的平方根吗 在这种明显错误的情况下会发生什么? 你写的功能是否能够处理这种低级错误。
直接来吧。回到测试中来,这次以一百万开始:
![逐步求精法][15]
图 5. 在计算25的平方根时运用逐步求精法以100万作为**bestGuess**的初始值。
哇! 以一个荒谬的数字开始迭代次数仅增加了两倍从八次迭代到23次。 增长幅度没有你直觉中预期的那么大。
### 故事的寓意
啊哈! 当你意识到,迭代不仅能够保证解决问题,而且与你的解决方案的初始猜测值是好是坏也没有关系。 不论你最初理解得多么不正确,迭代过程以及可衡量的测试/目标,都可以使你走上正确的道路并得到解决方案。
图4和5显示了陡峭而戏剧性的燃尽图。 一个非常错误得开始,迭代很快就产生了一个绝对正确的解决方案。
简而言之,这种神奇的方法就是敏捷 DevOps 的本质。
### 回到一些更深层次的观察
敏捷DevOps的实践源于人们对所生活的世界的认知。我们生活的世界存在不确定性不完整性以及充满太多的困惑。 从科学/哲学的角度来看,这些特征得到了[海森堡的不确定性原理][16] (涵盖不确定性部分), [维特根斯坦的逻辑论哲学][17] (歧义性部分), [哥德尔的不完全性定理][18] (不完全性方面), 以及[热力学第二定律][19] (无情的熵引起的混乱)的充分证明和支持。
简而言之,无论你多么努力,在尝试解决任何问题时都无法获得完整的信息。 因此,放下傲慢的姿态,采取更为谦虚的方法来解决问题对我们会更有帮助。 谦卑会给为你带来巨大的回报,这个回报不仅是你期望的一个解决方案,还会有它的副产品。
### 总结
大自然在不停地运作,这是一个持续不断的过程。 大自然没有总体规划。 一切都是对先前发生的事情的回应。 反馈循环是非常紧密的,明显的进步/倒退都是逐步实现的。大自然中随处可见,任何事物的都在以一种或多种形式逐步完善。
敏捷 DevOps 是工程模型逐渐成熟的一个非常有趣的结果。 DevOps 基于这样的认识,即你所拥有的信息总是不完整的,因此你最好谨慎进行。 获得可衡量的测试(例如,假设,可测量的期望结果),进行简单的尝试,大多数情况下可能失败,然后收集反馈,修复故障并继续测试。 除了同意每个步骤都必须要有可衡量的假设/测试之外,没有其他方法。
在本系列的下一篇文章中,我将仔细研究变异测试是如何提供及时反馈来推动实现结果的。
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/8/mutation-testing-evolution-tdd
作者:[Alex Bunardzic][a]
选题:[lujun9972][b]
译者:[Morisun029](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/alex-bunardzic
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_520X292_openanttrail-2.png?itok=xhD3WmUd (Ants and a leaf making the word "open")
[2]: https://opensource.com/article/19/7/failure-feature-blameless-devops
[3]: https://en.wikipedia.org/wiki/Test-driven_development
[4]: https://www.merriam-webster.com/dictionary/conditio%20sine%20qua%20non
[5]: https://opensource.com/sites/default/files/uploads/darwin.png (Charles Darwin)
[6]: https://en.wikipedia.org/wiki/Charles_Darwin
[7]: https://en.wikipedia.org/wiki/On_the_Origin_of_Species
[8]: https://opensource.com/sites/default/files/uploads/environmentalconditions2.png (Environmental pressures on fish)
[9]: https://www.youtube.com/watch?v=MgK5Rf7qFaU
[10]: https://dotnet.microsoft.com/
[11]: https://xunit.net/
[12]: https://opensource.com/sites/default/files/uploads/xunit-output.png (xUnit output after the unit test run fails)
[13]: https://opensource.com/sites/default/files/uploads/unit-test-success.png (Unit test successful)
[14]: https://opensource.com/sites/default/files/uploads/iterating-square-root.png (Code iterating for the square root of 25)
[15]: https://opensource.com/sites/default/files/uploads/bestguess.png (Stepwise refinement)
[16]: https://en.wikipedia.org/wiki/Uncertainty_principle
[17]: https://en.wikipedia.org/wiki/Tractatus_Logico-Philosophicus
[18]: https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems
[19]: https://en.wikipedia.org/wiki/Second_law_of_thermodynamics