《Cucumber:行为驱动开发指南》——2.8 让测试通过

简介: 事实上,这与我们之前说的不完全一样,我们说希望做能让测试通过的最少的、有用的工作。这里我们做的确实让测试通过了,但并不十分有用,暂且不说它根本没有计算器的功能这一事实,我们先看看这行耍小聪明的代码在测试时漏掉了什么。

本节书摘来自异步社区《Cucumber:行为驱动开发指南》一书中的第2章,第2.8节,作者:【英】Matt Wynne , 【挪】Aslak Hellesy著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.8 让测试通过

既然已经有了可靠的失败场景,那就是时候让这个Cucumber场景指导我们编写解决方案了。

有一个非常简单的方案能让测试通过,但该方案其实不会有实际的帮助,不管怎样我们先试一下,哪怕为了好玩儿:

下载first_taste/08/calc.rb
print "4"

试试运行cucumber,你会看到场景最终通过了:

...

1 scenario (1 passed)
3 steps (3 passed)
0m0.025s

很好!不过这个方案有什么问题呢?毕竟我们说过希望做能让测试通过的最少的工作,对不对?

事实上,这与我们之前说的不完全一样,我们说希望做能让测试通过的最少的、有用的工作。这里我们做的确实让测试通过了,但并不十分有用,暂且不说它根本没有计算器的功能这一事实,我们先看看这行耍小聪明的代码在测试时漏掉了什么。

  •  我们没有尝试从命令行读取输入。
  •  我们没有尝试做加法运算。
    在《Crystal Clear:小团队的敏捷开发方法》[Coc04]一书中,Alistair Cockburn提倡在项目中尽早构建一个可行走的骨架(walking skeleton),以便发现技术选型的任何潜在问题。显然我们的计算器非常简单,但一样值得我们考虑一下这条原则:为什么我们不构建一种能通过该场景的更有用的东西,并且让它帮助我们更多地了解自己打算使用的实现呢?

如果你不能信服这种观点,可以尝试将这种解决方案看成一个代码重复的问题。我们在两个地方硬编码了4这个值:一处是场景中,另一处是计算器程序中。在更复杂的系统中,类似的重复可能不会被注意到,因而使场景变得脆弱。

让我们强迫自己修复这个问题,可以使用Kent Beck在《测试驱动开发》一书中所说的三角法(triangulation)。我们使用一个新的名为Scenario Outline(场景轮廓)的关键字来为特性添加另一个场景:

下载first_taste/09/features/adding.feature
Feature: Adding

Joe问:我觉得很怪异,你一直在让测试通过但毫无作用!

我们实现了一个步骤,它调用了计算器程序然后就通过了,即便这个时候“计算器”还只是一个空文件。这到底是怎么回事?

记住一个步骤本身并不是一个测试,测试是整个场景,除非所有步骤都通过了,否则场景不可能通过。在我们实现所有步骤之后,只有一种办法能让整个场景通过,那就是构建一个能运行加法运算的计算器!

像这样由外向内工作的时候,我们常常会使用空计算器程序这样的桩(stub),把它作为一个将来需要填充的预留区域。我们知道不可能永远把空文件留在那里并侥幸成功,因为最终Cucumber会告诉我们,要让整个场景通过就必须回来给空文件夹添加内容,使它能做点有用的事。

故意只做能让测试通过的最少的、有用的工作,这一原则看起来很懒,但实际上是一条纪律,它能保证我们的测试彻底且周密:如果测试没有驱动我们编写正确的软件,那么我们就需要更好的测试。

Scenario Outline: Add two numbers 
 Given the input "<input>"

 When the calculator is run

 Then the output should be "<output>"

 Examples:
  | input | output | 
  | 2+2  | 4   | 
  | 98+1 | 99   |

我们把场景转变成了场景轮廓,这使我们可以用表格指定多个场景。另一种方法是复制并粘贴原来的整个场景然后改变其中的一些值,但我们认为使用场景轮廓在表述实例方面可读性更强,并且我们想让你体验一下Gherkin语法允许的其他写法。我们看一下现在输出是什么样子:

$ cucumber

Feature: Adding

 Scenario Outline: Add two numbers   
  Given the input "<input>"      
  When the calculator is run      
  Then the output should be "<output>" 

  Examples: 
   | input | output |
   | 2+2  | 4    |
   | 98+1  | 99   |
   expected: "99"
      got: "4" (using ==) (RSpec::Expectations::ExpectationNotMetError)
   ./features/step_definitions/calculator_steps.rb:15
   features/adding.feature:6

Failing Scenarios:
cucumber features/adding.feature:3 # Scenario: Add two numbers
2 scenarios (1 failed, 1 passed)
6 steps (1 failed, 5 passed)
0m0.072s

从摘要中的 2 scenarios (1 failed, 1 passed),我们可以看到Cucumber已经运行了两个场景,Cucumber运行场景轮廓的时候,它会把实例(Examples)表中的每一行扩展成一个场景。第一个实例(结果为4的那个)仍然通过了,但是第二个实例失败了。

现在,肯定应该用一个更切实的解决方案重新实现我们的程序了:

下载first_taste/10/calc.rb
print eval(ARGV[0])

首先我们读取命令行参数ARGV[0],然后把它传给Ruby的eval方法。这足以算出与计算器输入相关的结果,最后我们将结果打印到终端。

试一下,是不是两个场景都通过了?很好!你已经构建了自己的第一个用Cucumber驱动的应用程序。

相关文章
|
测试技术 Python
单元测试工具(连载8)
单元测试工具(连载8)
87 0
|
敏捷开发 自然语言处理 Java
Cucumber -基于 behave 自动化测试指南 (一)
基于 behave 自动化测试系列教程
|
测试技术 持续交付 容器
|
XML 测试技术 C++
|
程序员 测试技术 数据库
《Cucumber:行为驱动开发指南》——6.3 照管好你的测试
自动化特性的好处在于你可以把它们作为活文档来长期信赖,因为你会将每一个场景都用于检查产品代码,以确保它们仍然有效。对于同代码打交道的程序员来说,这还有另一件好处:在他们开发系统的时候,那些测试可以充当安全网,对任何破坏已有行为的错误都给出警告。
2332 0
|
Ruby
《Cucumber:行为驱动开发指南》——1.4 Cucumber如何工作
Cucumber是一个命令行工具。运行时它会从普通语言编写的称为特性(feature)的文本文件中读取你的规格说明,解析需要测试的场景(scenario),然后针对你的系统运行这些场景以达到测试的目的。
2161 0
|
Ruby
《Cucumber:行为驱动开发指南》——2.5 运行程序
这段代码试图运行我们的计算器程序calc.rb,同时传入上一个步骤保存下来的输入,并用另一个实例变量保存输出。接下来它检查一个名字很特别的(实际上是很隐晦的)Ruby变量$?,来核实命令是否成功运行了,如果运行失败则抛出一个错误。
1573 0