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

    xiaoxiao2024-02-19  139

    本节书摘来自异步社区《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驱动的应用程序。

    相关资源:Cucumber 行为驱动开发指南 [(英)韦恩,(挪)赫勒索著]
    最新回复(0)