cucumber java从入门到精通(2)用代码定义步骤
上一节里我们定义了feature文件,feature文件就是自然语言描述的用例文件,它有一定的章法,具体的潜规则是:
- 使用Feature关键字定义定义功能名称
- 使用Scenaio关键字定义定义测试场景名称
- 使用Given关键字定义定义前置条件
- 使用When关键字定义定义测试步骤
- 使用Then关键字定义定义断言
Feature文件是测试人员与客户/产品经理进行需求交流的文档工具,定义好Feature文件以后,我们的测试功能点实际上已经是定义完成了,下面的步骤很自然就是用代码去实现这些测试点。当然这里要说明的是,我们用人肉手点这些测试功能点也是没有太大问题的,但是想一想我们所处的开发阶段,现阶段我们只有整理成feature的原始需求,我们的系统还是一穷二白,一行代码都没有,因此用手点这个万能利器我们是无法使用的了。 再来思考一下如果我们使用代码去定义测试用例有什么好处?尽管系统目前没有任何功能,但这并不能妨碍我们把自动化的测试用例给写好(很大程度上,这是自动化的单元测试用例),有了用例,我们就有了标准。在后面的流程中只要开发人员编写的代码能够跑通这些自动化用例,那么我们可以比较有信心的认为开发实现了客户/产品经理所提出的需求。另外一点就是,自动化测试用例写完是可以重复使用的,在日后的回归测试、验收测试和持续集成的过程中,这些用例将起到保障产品/代码质量的关键作用。
使用cucumber定义用例(Step)
回到命令行,我们在cucumber_first\step_definitions
文件夹下新建1个名为TodoStep.java
的文件
type nul > step_definitions\TodoStep.java
用文本编辑器编辑该文件。在教程的开始阶段我们并不需要使用Java IDE,因此你不需要纠结到底是使用eclipse还是intellij。在windows系统上,比较推荐的文本编辑器有Notepad++, sublime, gvim
。当然人各有所其所好,选择任意一种都是可以的,不过因为sublime和gvim在设置文件编码上面没有Notepad++直观,因此对于初学者来说使用Notepad++是一种比较稳妥的解决方案。
先把上一节中cucumber提示我们的那一大串代码片段复制进TodoStep.java
,然后加入包名和依赖包的导入,你的TodoStep.java
文件应该是这样的:
package step_definitions;
import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
public class TodoStep {
@假设("^我的任务清单里有(\\d+)个任务$")
public void 我的任务清单里有_个任务(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@当("^我完成(\\d+)件任务之后$")
public void 我完成_件任务之后(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@那么("^我还剩下(\\d+)件未完成的任务$")
public void 我还剩下_件未完成的任务(int arg1) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
看上去哪里有点奇怪。的确如此,我们的方法名是中文的,这不太符合一般的代码审美。
重构方法名及参数名
是时候重构一下方法名和参数名了。这次我们重构的驱动力有下面2个:
- 方法名是英文会让代码看上去更整洁;
- 给方法的参数取一个更容易理解的名称会让代码在日后更容易维护。
重构完成以后,TodoStep.java
中的代码应该是这样的:
package step_definitions;
import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
public class TodoStep {
@假设("^我的任务清单里有(\\d+)个任务$")
public void iHaveSomeTasks(int totalTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@当("^我完成(\\d+)件任务之后$")
public void iFinishSomeTasks(int finishedTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
@那么("^我还剩下(\\d+)件未完成的任务$")
public void iLeftSomeTasks(int leftTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
throw new PendingException();
}
}
重构完成之后我们发现我们的代码目的现在一目了然,增加了适当的可维护性。
注意:稳妥起见,请将TodoStep.java
文件的编码改为gb2312。
运行steps
运行我们刚定义的steps需要2个步骤
- 编译
TodoStep.java
文件 - 运行feature文件的同时指定与feature文件相关的step文件所在的路径
看起来有点绕,其实在命令行中不难完成
首先执行编译命令:
# windows
javac -cp "./jars/*;." step_definitions\TodoStep.java
# linux or unix
# javac -cp "./jars/*:." step_definitions\TodoStep.java
注意到我们把当前路径.
加到了CLASSPATH里,这个很容易漏掉。
然后运行feature文件
java -cp "./jars/*;." cucumber.api.cli.Main -g step_definitions features
如果没有错误的话,你会看到下面的信息
java -cp "./jars/*;." cucumber.api.cli.Main -g step_definitions features
P--
1 Scenarios (1 pending)
3 Steps (2 skipped, 1 pending)
0m0.134s
cucumber.api.PendingException: TODO: implement me
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:11)
at ?.假设我的任务清单里有3个任务(todo.feature:6)
这个提示信息告诉我们,我们定义了1个Scenario和3个Steps,不过我们并没有实现这些。因为我们的实现方法里只是简单的抛出了1个Pending异常。
Pending异常很像是占位符,它告诉代码的维护者这个测试步骤应该实现,只是现在还没开工而已。
### 自动化渐进
每次都在命令行里敲一长串命令除了会让你显得技术高超手法娴熟以外其实并不是什么特别有效率的事情(忽然想起来每次在命令行里用vim编辑文件各种前后台切换时总会有人发出惊叹)。而且命令越长出错的几率越大。
为什么不让这些命令自动执行呢?
先创建1个名为```compile.bat```的文件,我们用批处理的方式来自动运行命令。编辑之:
type nul > compile.bat
compile.bat
javac -cp "./jars/*;." step_definitions\TodoStep.java
再创建1个名为```run.bat```的文件,输入如下内容
type nul > run.bat
run.bat
java -cp "./jars/*;." cucumber.api.cli.Main -p pretty -g step_definitions features
现在我们可以把繁冗的命令简化成下面2个简单的指令了
compile
run
如果你是在linux或unix机器上,你可以这样做
touch compile
touch run
echo 'javac -cp "./jars/:." step_definitions\TodoStep.java' > compile
echo 'java -cp "./jars/:." cucumber.api.cli.Main -p pretty -g step_definitions features ' > run
chmod +x compile
chmod +x run
./compile
./run
```
总结
目前为止我们知道了2件事情
- cucumber先定义feature文件,该文件是用自然语言描述的
- 接下来要定义feature文件对应的step文件,这一步是用代码实现的
下一节我们将实现我们的第1个cucumber step及断言