摘要
本文从实际使用Cucumber这一工具的角度,以Cucumber-JVM实现为基础,采用了不同的事例阐述了:如何编写feature文件,如何从feature文件生成对应的Steps,如何生成不同格式的报告,如何定制化的运行测试用例以及在与其他主流工具结合中如何避免报告失真、如何与主流持续集成工具结合使用等,为大家在日常工作中使用Cucumber释疑解惑。
Cucumber是什么
Cucumber是BDD模式下实现可执行规范(Executable Specifications)的开源工具,但是它的使命并不局限于做自动化验收测试,更加重要的在于其能够在团队成员之间构建统一的交流基础(feature文件)、规范交流用语(Domain Specific Language)、提高各个利益相关方(Business Stakeholders)沟通效率和效果从而达到提升产品质量、做成客户期望得到的产品这一最终目标。
如何使用Cucumber
Cucumber有很多种语言的实现版本,例如Java、Ruby、.NET、JavaScript等等,并且Cucumber可以和主流的测试框架很好地集成,常见的Selenium、SpringFramework、Ruby on Rails等,能够方便地引入到你的测试工作中去,几乎没有任何门槛。以一个Java测试项目为例,介绍如何使用Cucumber的Java语言实现版本:Cucumber-JVM。
将Cucumber-JVM依赖加入到项目中
如果你的项目是使用Maven管理所依赖的第三方依赖jar包,那么引入Cucumber-JVM将是一件优雅而且轻松的事情,只需要简单的将如下的Code Snippet加入到项目的pom.xml的“dependencies”下即可:info.cukescucumber-java${cucumber.version}testinfo.cukescucumber-junit${cucumber.version}testjunitjunit${junit.version}test1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
关于version,请尽量选择适合自己项目的,如果你使用的是Java 7,如果没有特殊的要求可以与本文一样采用1.2.2
编写 Executable Specification
Cucumber之所以受到如此的推崇,与其Executable Specification这个特性不无关系。顾名思义,可执行规范给出了至少两个方面的意义:
- 可执行性(Executable):你可以像执行代码(Java、Ruby…)一样运行这些规范,来验证、验收目标应用。当然,这一点是从技术人员的视角来看的;
- 规范性(Specification):从非技术人员的视角触发,相比验证本身,他们更加关心系统功能的清晰描述:系统在什么场景下能够做什么样的事情。
这看似简单的两方面似乎关联并不是很大,但是如何能够在同一个基础(feature files)之上做到两者的融合,却是Cucumber最大的妙处。从项目管理人员的角度来看,Cucumber是技术人员和非技术人员交流的桥梁,从更加深的层面来看,Cucumber能够使增加各个利益相关方的沟通,因为只有深入的沟通,在各方都理解了真正期望的功能这一基础之上,才能产出都认可的Executable Specification!
回归到工具这一层面,Cucumber是以feature文件来组织测试的,相信大家都很清楚这里之所以采用feature这个后缀,其实正是为了凸显用户在使用系统中所能够享受到的服务和功能。以ATM取钱场景为例子,通过如下的大致步骤:
- 创建feature文件,
- 生成测试Step Definitions,
- 运行测试用例。
来具体说明如何在测试工作中使用Cucumber。
创建Feature文件
自动柜员机(ATM)大家都非常熟悉,现在假设你在为某一个银行所提供的固定金额取款功能编写测试用例,经过跟利益相关方讨论之后,针对这一功能,你们得出了如下的场景定义,此处以Feature文件的形式写出来:
src/main/resources/features/FixedAmountWithdraw.feature# language: zh-CN功能: 使用ATM固定金额方式取款 通常“取款”菜单包含了几个固定金额,使用这些固定金额取款可以避免从键盘输入提取金额,从而可以加速交易,提高取款的效率。 场景大纲: 固定金额取款 假如 我的账户中有余额""元 当 我选择固定金额取款方式取出""元 那么 我应该收到现金""元 而且 我账户的余额应该是""元 例子: | accountBalance | withdrawAmount | receivedAmount | remainingBalance | | 1000.00 | 100.00 | 100.00 | 900.00 | | 500.00 | 500.00 | 500.00 | 0.00 |1
2
3
4
5
6
7
8
9
10
11
12
13
上述文件中,需要说明的是:
- 1,# language: zh-CN表明feature文件中所使用的描述语言是中文简体,Cucumber本身支持超过30种语言(此处语言是Spoken Language而非Programming Language)
- 通过java cucumber.api.cli.Main --i18n help查看所支持的所有Spoken Language;
- 通过java cucumber.api.cli.Main --i18n LANG查看所给定语言支持的所有关键字,比如查看中文简体(zh-CN)支持的所有Gherkin关键字,可以通过执行命令java cucumber.api.cli.Main --i18n zh-CN查看,其输出类似如下内容:| feature | "功能" | | background | "背景" | | scenario | "场景", "剧本" | | scenario_outline | "场景大纲", "剧本大纲" | | examples | "例子" | | given | "* ", "假如", "假设", "假定" | | when | "* ", "当" | | then | "* ", "那么" | | and | "* ", "而且", "并且", "同时" | | but | "* ", "但是" | | given (code) | "假如", "假设", "假定" | | when (code) | "当" | | then (code) | "那么" | | and (code) | "而且", "并且", "同时" | | but (code) | "但是" |1
2
3
4
5
6
7
8
9
10
11
12
13
14
152,采用中文描述feature文件,首先得益于Cucumber本身的支持,但是另外一个最重要的原因是我期望所描述的功能可以被利益相关方清楚地读懂,使用利益相关方的Spoken Language来撰写规范可以使沟通更加方便,是Cucumber的又一大利器;
3,如果不使用# language: zh-CN这个header,默认情况下,Cucumber会以英语解析feature文件寻找场景和Steps,此时,Gherkin的关键词必须使用英文:Feature: 使用ATM固定金额方式取款 通常“取款”菜单包含了几个固定金额,使用这些固定金额取款可以避免从键盘输入提取金额,从而可以加速交易,提高取款的效率。 Scenario Outline: 固定金额取款 Given 我的账户中有余额""元 When 我选择固定金额取款方式取出""元 Then 我应该收到现金""元 And 我账户的余额应该是""元 Examples: | accountBalance | withdrawAmount | receivedAmount | remainingBalance | | 1000.00 | 100.00 | 100.00 | 900.00 | | 500.00 | 500.00 | 500.00 | 0.00 |1
2
3
4
5
6
7
8
9
10
11
124,使用Cucumber dry run命令java cucumber.api.cli.Main -d src/main/resources/features/FixedAmountWithdraw.feature可以验证对应的feature文件内容是否有格式错误,同时,这个命令也会打印出dummy的steps definitions,参考如下的输出内容:
注:命令java cucumber.api.cli.Main 事实上要求加入对应的jar包到classpath中,为了行文方便,此处省略了对应的classpath。建议读者在使用时,先把对应的jar包下载到本地(推荐使用maven,执行mvn compile即可下载对应的dependency到本地的.m2目录下),然后根据所使用的操作系统不同,可以按照:alias cucumber='java -classpath ".:./target/classes:/home/admin/.m2/repository/info/cukes/cucumber-java/1.2.2/cucumber-java-1.2.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-core/1.2.2/cucumber-core-1.2.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-jvm-deps/1.0.3/cucumber-jvm-deps-1.0.3.jar:/home/admin/.m2/repository/info/cukes/gherkin/2.12.2/gherkin-2.12.2.jar:/home/admin/.m2/repository/info/cukes/cucumber-junit/1.2.2/cucumber-junit-1.2.2.jar:/home/admin/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/admin/.m2/repository/info/cukes/cucumber-html/0.2.3/cucumber-html-0.2.3.jar" cucumber.api.cli.Main'1
– 对于Windows,可以在PATH指定的任意一个目录(比如 C:\Windows\System32\)下,创建一个cucumber.bat,其中的内容可以参考如下:java -classpath ".;.\target\classes;C;\Users\admin\.m2\repository\info\cukes\cucumber-java\1.2.2\cucumber-java-1.2.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-core\1.2.2\cucumber-core-1.2.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-jvm-deps\1.0.3\cucumber-jvm-deps-1.0.3.jar;C;\Users\admin\.m2\repository\info\cukes\gherkin\2.12.2\gherkin-2.12.2.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-junit\1.2.2\cucumber-junit-1.2.2.jar;C;\Users\admin\.m2\repository\junit\junit\4.12\junit-4.12.jar;C;\Users\admin\.m2\repository\info\cukes\cucumber-html\0.2.3\cucumber-html-0.2.3.jar" cucumber.api.cli.Main %*1
生成Step definitions
当然,通过上述的dry run产生的dummy steps definitions可以作为创建对应Step definitions的Skeleton,在没有IDE协助你生成Step definitions的前提下,我推荐大家使用dry run的输出作为基础,创建自己的Step definitions。此处,本文推荐大家使用集成开发环境所提供的Step definitions工具来实现上述操作,其原因是:
- 1,集成开发工具能够生成较为完善的代码,不仅仅是steps definitions本身,而且包含了对应的Java Class类;
- 2,对于刚接触Cucumber不久且充满好奇心的使用者而言,能够接触到Cucumber的实现代码,更加有利于了解其运行原理,而集成开发环境在这一点上能够提供很大的帮助。
由于Eclipse暂时并不支持Steps definitions的生成操作,下面本文以JetBrain的优秀Java IDE环境Intellij IDEA述如何实现从feature生成Steps definitions。
在Intellij IDEA下生成Steps definitions
在编写feature文件的过程中,IDEA会提示目前文件中哪些步骤(steps)是没有对应的Java step definitions,如下图所示,Intellij IDEA会以黄色的小灯泡这个提示标志来提醒作者:
作者只需要按照如下的步骤来生成对应的Steps definitions即可:
- 1,点击该提示图标,并从弹出的菜单项中选择“Create Step Definition”或者“Create All Steps Definition”;
- 2,在弹出的“Create New Step Definition File”模式窗口中填写文件名称、实现语言以及文件位置等信息即可;
- 3,重复步骤1和2直到所有的step都生成对应的Java step definition即可。
package io.cucumber.samples.dw.steps; import cucumber.api.PendingException; import cucumber.api.java.zh_cn.*; public class FixedAmountWithdrawStepdefs { @那么("^我应该收到现金\"([^\"]*)\"元$") public void 我应该收到现金_元(String accountBalance) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @假如("^我的账户中有余额\"([^\"]*)\"元$") public void 我的账户中有余额_元(String withdrawAmount) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @当("^我选择固定金额取款方式取出\"([^\"]*)\"元$") public void 我选择固定金额取款方式取出_元(String receivedAmount) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @而且("^我账户的余额应该是\"([^\"]*)\"元$") public void 我账户的余额应该是_元(String remainingBalance) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在Eclipse下生成Steps definitions
在Eclipse中,仍然可以通过Dry run的方式来实现部分steps definitions code的生成,这一点请参考前文中Cucumber dry run命令。
在IDEA中调试运行测试用例
Cucumber测试用例有两种方式可以启动,debug mode下,两种方式都可以对Steps definitions代码进行debug:
- 1,Run with JUnit方式;
这种方式要求必须有JUnit Test来触发,常规的做法是:
- a,创建一个空白的JUnit Test:所谓空白就是在JUnit Test类中没有任何方法;
- b,按照Cucumber所提供的Options annotation来引入所需要使用的feature文件和Steps definitions类;
- c,按照需要设置报告格式等,下面是以JUnit方式触发FixedAmountWithdraw.feature文件的配置Sample;package io.cucumber.samples.dw; import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; import org.junit.runner.RunWith; @RunWith(Cucumber.class) @CucumberOptions( format = {"json:target/json-report/dw.json"} , features = {"classpath:features/FixedAmountWithdraw.feature"} , glue = {"io.cucumber.samples.dw.steps"} ) public class AppTest { }1
2
3
4
5
6
7
8
9
10
11
12
13
14d,然后就可以以JUnit用例的方式运行或者调试测试用例的实现了,此处不再赘述。
Eclipse和Intellij IDEA都支持这样的调试方式
2,IDEA下直接运行Scenario/Scenario Outline
a,IDEA下可以直接选中所需要运行或者调试的Scenario/Scenario Outline;
b,然后右键打开弹出菜单选择“Run”或者“Debug”;
c,并选择对应的Scenario名称即可运行或者调试该场景对应的Steps definitions。
定制化运行Cucumber测试用例
Cucumber是以feature文件来组织测试用例的,关于如何customized运行Cucumber测试用例,首先需要向大家介绍就是如何组织feature文件。Feature文件通常是按照子文件夹来组织的,你可以按照功能归属、类别、Sprint Plan等等来做,至于哪一种是最好的,通常需要并没有准确的答案,一个可行的建议是先按照你认为最可行的方式组织起来,尝试一段时间,持续一两个iteration,后续如果没有发现明显的问题就可以坚持下来。通常你需要尝试几次才能确定哪一种方式最适合自己的team。子文件夹就像是一本书的目录对应的章节,便于索引。
Cucumber还支持为Scenario指定标签(tag),tag是以“@”字符开头的一个单词,用来表述被修饰对象(可以是feature,可以是scenario,可以是scenario outline甚至可以是scenario outline下的examples)所属的类别。Tag可以是一个,也可以是多个,Cucumber本身并不限制tag的个数。# language: zh-CN@withdraw功能: 使用ATM固定金额方式取款 通常“取款”菜单包含了几个固定金额,使用这些固定金额取款可以避免从键盘输入提取金额, 从而可以加速交易,提高取款的效率。 @fixedAmount 场景大纲: 固定金额取款 假如 我的账户中有余额""元 当 我选择固定金额取款方式取出""元 那么 我应该收到现金""元 而且 我账户的余额应该是""元 @positive 例子: | accountBalance | withdrawAmount | receivedAmount | remainingBalance | | 1000.00 | 100.00 | 100.00 | 900.00 | | 500.00 | 500.00 | 500.00 | 0.00 | @negative 例子: | accountBalance | withdrawAmount | receivedAmount | remainingBalance | | 1000.00 | 1100.00 | 0.00 | 1000.00 | | 500.00 | 600.00 | 0.00 | 500.00 |1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
其中Tag修饰对象 表述的意义Feature 该Feature文件中的所有Scenario和Scenario Outline都会继承修饰在Feature的tag,比如上例中的@withdraw
Scenario / Scenario Outline 表示tag适用于Scenario/Scenario Outline,Scenario Outline下的Examples会继承定义在其上的tag,比如上例中的@fixedAmount
Examples 只对Scenario Outline及当前的Examples有效,比如上例中的@positive和@negative
如果说子文件夹是目录对应的章节,那么tag可以看做是添加到书中的便签,让你迅速找到对应的scenario。Tag可以看做是另外一种维度的索引,可以为Cucumber测试用例集合添加不同的维度,从而便于检索和过滤,这一点在选择性地运行Cucumber测试用例时显得尤其重要!
如何与持续集成工具集成
主流的持续集成工具有很多,被广泛采用的开源工具当推Jenkins。Cucumber reporting功能也可以被Jenkins支持,Github中有开源的Jenkins Plugin:Publish pretty cucumber-jvm reports on Jenkins。对于其具体的用法,其文档中已经有详细介绍,作者尝试过可以按照其步骤成功集成Cucumber Reporting功能到Jenkins,此处不再赘述。
结束语
本文从实际使用Cucumber这一工具的角度,以Cucumber-JVM实现为基础,采用了不同的事例阐述了:如何编写feature文件,如何从feature文件生成对应的Steps,如何生成不同格式的报告,如何定制化的运行测试用例以及在与其他主流工具结合中如何避免报告失真、如何与主流持续集成工具结合使用等,为大家在日常工作中使用Cucumber释疑解惑。回到Cucumber作为自动化工具的层面,其在API测试中的使用也有很多可圈可点之处,本系列后续文章会对此进行阐述:
- 《活用Cucumber测试服务端开放API》讲述结合BDD风格测试工具Rest-Assured验证API功能,如何使用JSON Schema验证API的返回结构等一系列实用的经验分享。
下载
指定Cucumber运行结果报告
Cucumber本身支持多种报告格式以适用于不同环境下调用的报告输出:
- pretty :用于在命令行环境下执行Cucumber测试用例所产生的报告,如果你的console支持,pretty形式的报告还可以按照颜色显示不同的运行结果;如下图所示的例子分别显示了用例执行通过和用例没有Steps definitions的输出报告:
json :多用于在持续集成环境下的夸机器生成报告时使用,比如在用例执行的机器A上运行Cucumber测试用例,而在调度或报告机器B上生成用例执行报告,此时只需要把生成的json报告传输到机器B上即可。
html :用于生成简单的HTML格式的报告以便查看Cucumber测试用例运行的结果
junit :用于生成JUnit格式的报告<?xml version="1.0" encoding="UTF-8" standalone="no"?> 1
2
3
4
5
6
7
8
9
10
11
12
13