cucumber java从入门到精通(4)Scenario Outline及数据驱动

到目前为止,我们的TodoList类工作良好,不过离我们的预期——任务清单系统还是有不少差距,究其原因不过如下:

  • 我们的feature不太完毕,没有测试任务清单的增删改查完成等功能;
  • 我们输入的数据太过单一,只测试了1种输入输出的情况;

下面我们将着手解决数据输入太过单一的问题。我们将使用Scenario Outline技术。

什么是Scenario Outline

什么是Scenario Outline呢,我们不妨先去命令行里看一下,在命令行中输入

java -cp "jars/*" cucumber.api.cli.Main --i18n zh-CN

这将得到cucumber关键字的翻译:

| feature          | "功能"                   |
| background | "背景" |
| scenario | "场景", "剧本" |
| scenario_outline | "场景大纲", "剧本大纲" |
| examples | "例子" |
| given | "* ", "假如", "假设", "假定" |
| when | "* ", "当" |
| then | "* ", "那么" |
| and | "* ", "而且", "并且", "同时" |
| but | "* ", "但是" |
| given (code) | "假如", "假设", "假定" |
| when (code) | "当" |
| then (code) | "那么" |
| and (code) | "而且", "并且", "同时" |
| but (code) | "但是" |

我们可以看到scenario outline被翻译成了场景大纲或者是剧本大纲。简单来说,场景大纲可以理解为同一个场景同一部戏,内容都一样,台词也一样,只是换了不同的演员来演。每个场景大纲定义了不同的演员列表,该场景的台词是一样,演员的动作也相同,只是每次换不同的演员去表演。所谓铁打的营盘流水的兵。因此,场景大纲里定义了几组演员,这个场景就要演几次。

cucumber的场景大纲不是用来定义演员的,而是用来定义数据的。大纲里定义几组数据,那么该场景就要跑几次。

增加Example

现在我们给我们的feature增加1组测试数据:

#language: zh-CN

功能:任务管理

    场景大纲: 完成任务
假设 我的任务清单里有<total>个任务
当 我完成<finished>件任务之后
那么 我还剩下<left>件未完成的任务 例子:
| total | finished | left |
| 3 | 1 | 2 |
| 5 | 1 | 4 |

运行一下

run

我们发现cucumber报错了:

#language: zh-CN
功能: 任务管理 场景大纲: 完成任务 # todo.feature:5
假设我的任务清单里有<total>个任务
当我完成<finished>件任务之后
那么我还剩下<left>件未完成的任务 例子: 场景大纲: 完成任务 # todo.feature:12
假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)
当我完成1件任务之后 # TodoStep.iFinishSomeTasks(int)
那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int) 场景大纲: 完成任务 # todo.feature:13
假设我的任务清单里有5个任务 # TodoStep.iHaveSomeTasks(int)
java.lang.AssertionError: expected:<3> but was:<5>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
at ?.假设我的任务清单里有5个任务(todo.feature:6) 当我完成1件任务之后 # TodoStep.iFinishSomeTasks(int)
那么我还剩下4件未完成的任务 # TodoStep.iLeftSomeTasks(int) Failed scenarios:
todo.feature:13 # 场景大纲: 完成任务 2 Scenarios (1 failed, 1 passed)
6 Steps (1 failed, 2 skipped, 3 passed)
0m0.166s java.lang.AssertionError: expected:<3> but was:<5>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
at ?.假设我的任务清单里有5个任务(todo.feature:6)

看起来密密麻麻,实际上的意思就是场景大纲里的第二组数据报错了,我们的TodoList的实现无法满足第二组数据,想想这也是应该的。

在这里要解释一下例子这个关键字。例子在英文里叫Example,是feature关键字。紧跟在例子后的一半都是1个数据列表,从上文可以看到,一目了然。数据列表有表头,表后下面的行跟着的是测试数据。

Scenario Outline运行流程

Scenario Outline的运行流程就是先从例子里读一行数据,然后根据该数据的column也即是表头,在steps里找到相应的<column>字段,用具体的数据进行替换。例子里有2行数据这个Scenario就会运行2次。因此我们上面的feature文件实际上是这样运行的:

假设 我的任务清单里有3个任务
当 我完成1件任务之后
那么 我还剩下2件未完成的任务 假设 我的任务清单里有5个任务
当 我完成1件任务之后
那么 我还剩下4件未完成的任务

重构并让用例通过

我们的用例已经测试出TodoList类的缺陷了,是时候重构一下了,先重构TodoStep.java文件

// TodoStep.java
package step_definitions; import cucumber.api.java.zh_cn.*;
import cucumber.api.PendingException;
import static org.junit.Assert.*;
import implementation.TodoList; public class TodoStep {
TodoList todo; @假设("^我的任务清单里有(\\d+)个任务$")
public void iHaveSomeTasks(int totalTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
todo = new TodoList();
todo.setTotalTaskCount(totalTasks);
} @当("^我完成(\\d+)件任务之后$")
public void iFinishSomeTasks(int finishedTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
todo.finishTask(finishedTasks);
} @那么("^我还剩下(\\d+)件未完成的任务$")
public void iLeftSomeTasks(int leftTasks) throws Throwable {
// Write code here that turns the phrase above into concrete actions
assertEquals(todo.getRestTasksCount(), leftTasks);
}
}

我们这里假设TodoList有1个setTotalTaskCount方法,该方法用来设置当前TodoList中task的总数。另外我们还把假设中的断言给去掉了,这是因为如果假设中有断言,那就意味着你连前置条件都不信任,既然前提条件都不能保证,那么下面的步骤就没有太多意义了,所以去掉会让步骤定义更加的符合逻辑。

再重构TodoList.java文件

package implementation;

public class TodoList {
int totalTaskCount;
int finishedTaskCount; public TodoList() {
totalTaskCount = finishedTaskCount = 0;
} public int getTotalTaskCount() {
return totalTaskCount;
} public void setTotalTaskCount(int count) {
totalTaskCount = count;
} public void finishTask(int count) {
finishedTaskCount = count;
} public int getRestTasksCount() {
return totalTaskCount - finishedTaskCount;
}
}

代码很简单,就不一一解释了。

运行一下

compile && run

结果如下,所有的step都pass了。

#language: zh-CN
功能: 任务管理 场景大纲: 完成任务 # todo.feature:5
假设我的任务清单里有<total>个任务
当我完成<finished>件任务之后
那么我还剩下<left>件未完成的任务 例子: 场景大纲: 完成任务 # todo.feature:12
假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)
当我完成1件任务之后 # TodoStep.iFinishSomeTasks(int)
那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int) 场景大纲: 完成任务 # todo.feature:13
假设我的任务清单里有5个任务 # TodoStep.iHaveSomeTasks(int)
当我完成1件任务之后 # TodoStep.iFinishSomeTasks(int)
那么我还剩下4件未完成的任务 # TodoStep.iLeftSomeTasks(int) 2 Scenarios (2 passed)
6 Steps (6 passed)
0m0.133s

总结

从上面的例子里我们就可以看出自动化对生产力的提升帮助巨大。假设我们还需要测试100组数据,如果人肉手点的话,那么执行用例的人自然是痛不欲生,而且我们也没有办法完全保证数据输入的准确性,毕竟老虎也会有打盹的时候,何况是人。但是如果用自动化测试的话,增加数据无非就是在数据表中增加一些行,工作量不是特别大,而且可以比较容易的检查出数据是否准确。

像这种以增加输入输出数据的方式增加用例的测试用例设计方法,我们可以称之为数据驱动。

下一节我们将进一步的实现项目工程学上的自动化,我们将使用maven来搭建cucumber项目。

cucumber java从入门到精通(4)Scenario Outline及数据驱动的更多相关文章

  1. cucumber java从入门到精通(2)用代码定义步骤

    cucumber java从入门到精通(2)用代码定义步骤 上一节里我们定义了feature文件,feature文件就是自然语言描述的用例文件,它有一定的章法,具体的潜规则是: 使用Feature关键 ...

  2. cucumber java从入门到精通(1)初体验

    cucumber java从入门到精通(1)初体验 cucumber在ruby环境下表现让人惊叹,作为BDD框架的先驱,cucumber后来被移植到了多平台,有cucumber-js以及我们今天要介绍 ...

  3. cucumber java从入门到精通(3)简单实现及断言

    cucumber java从入门到精通(3)简单实现及断言 上一节里我们定义了step的java代码实现文件,step就是测试步骤及断言的集合,我们先定义出来,以后可以驱动开发以及在持续集成时重用. ...

  4. 《JAVA 从入门到精通》 - 正式走向JAVA项目开发的路

    以前很多时候会开玩笑,说什么,三天学会PHP,七天精通Nodejs,xx天学会xx ... 一般来说,这样子说的多半都带有一点讽刺的意味,我也基本上从不相信什么快速入门.我以前在学校的时候自觉过很多门 ...

  5. 《Java从入门到精通》src0-8

    public class HelloWorld { public static void main(String[] args) { System.out.println("Hello wo ...

  6. 《java从入门到精通》学习记录

    目录 <Java从入门到精通>学习记录 3 基础的基础部分: 3 一. 常量与变量 3 1. 掌握: 3 (1) .常量与变量的声明方式: 3 (2) .变量的命名规则: 3 (3) .变 ...

  7. Java从入门到精通一步到位!

    Java作为近几年来非常火的编程语言,转行来做Java的人不计其数,但如今真正的人才仍然匮乏,所以学习Java一定要有一个系统的学习规划课程.阿里云大学帮您规划Java学习路线可以帮助您从一个小白成长 ...

  8. Java从入门到精通——基础篇之JSTL标签

    一.语言基础 EL(Expression Language)表达式,目的:为了使JSP写起来更加简单.提供了在 JSP 中简化表达式的方法. 二.分类 核心标签库:提供条件判断.属性访问.URL处理及 ...

  9. Java从入门到精通——基础篇之Servlet与JSP的区别

    一.基本概念 1.1 Servlet Servlet是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面.它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器 ...

随机推荐

  1. ArcGIS10.3新体验

    自2012年ESRI更新10.2以后,终于在2014年12月8日,官方推出了10.3版本,前几天忙于抢票,今天终于可以在虚拟机中体验一把. 由于使用的是预览版,所有安装包只有800多M,包括桌面核心程 ...

  2. ASP.NET匿名对象与集合的使用

    一.关于匿名类型的特性 1.匿名类型 定义:匿名对象 var vobj = new { id = "编号", title = "标题", content = & ...

  3. javascript 和 jquery插件开发

    window.onload 页面加载完成后执行 window.onload = function () { function dom(id) { return document.getElementB ...

  4. Go语言使用Beego的ORM插入Mysql后,时区不一致的解决方案

    一.解决方案: 方案一: orm.Debug = false orm.DefaultTimeLoc = time.UTC orm.RegisterDriver("mysql", o ...

  5. markdown table语法

    普通的表格 | 一个普通标题 | 一个普通标题 | 一个普通标题 | | ------ | ------ | ------ | | 短文本 | 中等文本 | 稍微长一点的文本 | | 稍微长一点的文本 ...

  6. 算法笔记_199:第二届蓝桥杯软件类决赛真题(C语言本科)

    前言:以下代码部分仅供参考,C语言解答部分全部来自网友,Java语言部分部分参考自网友,对于答案的正确性不能完全保证. 试题1 数论中有著名的四方定理:所有自然数至多只要用四个数的平方和就可以表示. ...

  7. 阿里历年经典Java面试题汇总,想进BAT你还不快收藏!

    转载:https://mp.weixin.qq.com/s/M8YyxloxZnMACH9QCQN7HA Volatile的特征: A.禁止指令重排(有例外) B.可见性 Volatile的内存语义: ...

  8. 视频转换ffmpeg

    使用yum在centos下安装ffmpeg   ffmpeg -i IMG_1893.MOV -ab 56 -ar 22050 -b 500 -r 15 -s 640x480 test.mp4   说 ...

  9. Spring Cloud Edgware SR3 让Zuul支持形如 /xxx和/xxx/yyy 格式的路径配置

    在包路径:org.springframework.cloud.netflix.zuul.filters 下,新建类SimpleRouteLocator,取代jar包中的类.内容如下: /* * Cop ...

  10. Ubuntu16.04下的stm32环境配置

    安装stlink 必须安装libusb-1.0-0-dev, 其他安装不起作用 -dev git clone https://github.com/texane/stlink.git cd stlin ...