Cucumber概念解析与Java入门实例 (中)

来源:互联网 发布:通信网络集成资质取消 编辑:程序博客网 时间:2024/05/16 11:47

在上一篇《Cucumber概念解析与Java入门实例 (上)》中,我们介绍了AAT,BDD,Cucumber的概念,并开始搭建了商品结算checkout实例,现在我们继续喽 : )

创建Step Definitions

我们只需将之前Cucumber输出的最后一个代码片段复制并粘贴到一个新的Java文件中,现在可以先不着急理解具体的含义。让我们创建一个新的文件夹来存放我们的step定义:

$ mkdir step_definitions

现在在step_definitions中创建一个名为CheckoutSteps.java的Java文件。在文本编辑器中打开它,完成Jar包的导入和类定义的雏形,并粘入之前终端输出的代码片段:

package step_definitions;import cucumber.api.java.en.*;import cucumber.api.PendingException;public class CheckoutSteps {    @Given("^the price of a \"(.*?)\" is (\\d+)c$")    public void thePriceOfAIsC(String arg1, int arg2) throws Throwable {        // Write code here that turns the phrase above into concrete actions        throw new PendingException();    }    @When("^I checkout (\\d+) \"(.*?)\"$")    public void iCheckout(int arg1, String arg2) throws Throwable {        // Write code here that turns the phrase above into concrete actions        throw new PendingException();    }    @Then("^the total price should be (\\d+)c$")    public void theTotalPriceShouldBeC(int arg1) throws Throwable {        // Write code here that turns the phrase above into concrete actions        throw new PendingException();    }}

我们现在要做的是运行Cucumber,以便继续让它告诉我们下一步该做什么,但在这之前,别忘了我们需要编译新的CheckoutSteps类并将其添加到我们的类路径中。这是通常在使用IDE工作时不需要操心的事,但这次是命令行纯手工制造,含金量更高喽!
修改之前名为cucumber的shell文件,以便编译我们的Java代码的同时调用Cucumber:

javac -cp "jars/*" step_definitions/CheckoutSteps.javajava -cp "jars/*:." cucumber.api.cli.Main -p pretty --snippets camelcase \         -g step_definitions features

特别留意一点:我在以上书写的Cucumber的shell脚本,这是面向*nix用户的。Java类路径的语法取决于底层操作系统。 对于*nix操作系统,分隔符是冒号( : ),而对于Windows操作系统,分隔符是分号( ; )。

第1行编译我们刚刚创建的CheckoutSteps类,第2行调用Cucumber。
Cucumber调用有两个细节上的增添:
1. 将当前目录“.”添加到类路径中。
2. 添加-g step_definitions命令行参数来告诉Cucumber在哪里找到step定义,而step定义正是负责将feature文件中的step与checkout应用程序(我们还没有实现)“粘连”在一起。

现在,让我们再次执行./cucumber,看看接下来需要做什么:
output

我们的scenario已经从”undefined”转为了”pending”状态。好消息喽!因为这意味着Cucumber现在正在运行第一个step,但是当它进行这里时,它执行到了我们复制和粘贴的step定义代码中throw new PendingException()的调用,其告诉了Cucumber这个scenario是仍在编写中的。我们需要用真正的实现来替换这个抛出的异常。
请注意,Cucumber报告其他两个step是被跳过的”skipped”状态。 一旦遇到failed或pending的step,Cucumber将停止运行scenario,并跳过剩余的step。
我们来实现第一个step定义。

实现第一个step定义

我们已经考虑好checkout的第一个版本将是一个类,其包含一个把价格表和购买的项目作为传参的方法。所以,我们在对于Given the price of a banana is 40c的step定义的任务就是记住香蕉的价格。
在step_definitions文件夹中,编辑CheckoutSteps.java文件,使得第一个step定义如下所示:

@Given("^the price of a \"(.*?)\" is (\\d+)c$")public void thePriceOfAIsC(String name, int price) throws Throwable {    int bananaPrice = price;}

再次执行./cucumber,得到结果如下:
output

耶!我们的第一个step通过了!不过scenario仍然被标记为”pending”,因为我们还有两个step没有实现。

改变Cucumber的输出

每当我们运行Cucumber时,都可以留意一下其输出中feature的整个内容。让我们切换到使用progress插件来获得更突出的输出吧。编辑脚本文件cucumber的输出部分,使整体变成这样:

javac -cp "jars/*" step_definitions/CheckoutSteps.javajava -cp "jars/*:." cucumber.api.cli.Main -p progress --snippets camelcase \        -g step_definitions features

再次运行./cucumber,可看到输出变成了如下这样:
output

相比于打印出整个feature,progress插件在输出中只打印出三个字符,每个step对应一个。第一个.字符意味着通过的step。P字符意味着第二个步骤正在pending。最后的-字符意味着最后一步被skipped。
Cucumber有几种不同的插件,可以产生不同格式的输出,具体可使用指令
java -cp "jars/*" cucumber.api.cli.Main --help查看。

测试Checkout类

编辑step_definitions/CheckoutSteps.java使得第二个step定义如这样:

@When("^I checkout (\\d+) \"(.*?)\"$")public void iCheckout(int itemCount, String itemName) throws Throwable {    Checkout checkout = new Checkout();    checkout.add(itemCount, bananaPrice);}

这段代码中我们试图在Checkout类的一个实例中调用add方法,以传递购买的项目及它们的价格。
这次运行./cucumber时,我们应该得到一个编译错误,因为我们还没有创建一个Checkout类:
output

我们的step没有通过,因为还没有定义一个可用的Checkout类。
我故意这样做是因为我们希望确保在着手项目开发之前,有一个功能全面的测试工具处于就绪状态。我们希望保持一种模式:我们可以相信测试工具的性能,因为我们已经看到它们提示了failed,这让我们可以确信,当测试显示pass时,我们确实完成的任务。而这其实正是outside-in development的重要组成部分。

现在我们创建一个文件夹来保存我们的类实现:

$ mkdir implementation

并在该文件夹中创建一个称为Checkout.java的文件,并用文本编辑器修改如下:

package implementation;public class Checkout {    public void add(int count, int price) {    }}

我们需要在脚本文件cucumber中加入对该类的编译,同时修改classpath为项目的根目录,最终文件整体如下:

javac -cp "jars/*:." step_definitions/CheckoutSteps.java \        implementation/Checkout.javajava -cp "jars/*:." cucumber.api.cli.Main -p progress --snippets camelcase \        -g step_definitions features

同时不要忘记将Checkout类import入CheckoutSteps类中:

import implementation.Checkout;

并将bananaPrice变为实例变量,以使得其可以在iCheckout()方法中被使用:

public class CheckoutSteps {    int bananaPrice = 0;    @Given("^the price of a \"(.*?)\" is (\\d+)c$")    public void thePriceOfAIsC(String name, int price) throws Throwable {        bananaPrice = price;    }

再次运行,结果如下:
output

第二个step也成功地通过喽。

我们将在下一篇《Cucumber概念解析与Java入门实例 (下)》中开始完成测试中重要的结果判断步骤。