Qtest测试之道 测试兵器谱の Cucumber-JVM 框架篇

360Qtest团队 · 2019年11月28日 · 最后由 陈巧林 回复于 2019年11月29日 · 5077 次阅读

写在前面

最近业务上使用的自动化测试项目在改进项目执行方案,优化框架,正好结合实践记录一下最近遇到的问题和解决方法,打算从以下几个部分跟大家探讨一下:

  • 认识一下 Cucumber-JVM 框架
  • 基于 Anyproxy 代理多端口分发
  • Appium 多端口启动设备
  • Cucumber 分布式执行设备调度和任务分发
  • Maven 的一些配置问题
  • Pipeline 集成 Cucumber

关于 Cucumber-JVM

项目背景

目前的自动化方案是针对我们广告 SDK 项目来做的,之前关注过 Qtest 公众号的同学应该看到过一篇文章介绍过《广告 SDK 自动化测试实践》。简单说一下当时为什么这么选择这个架构体系。

  1. 我们的自动化测试项目是针对 Android SDK 来做的,因此在一些 API 调用上优先考虑了 java 方向的框架,希望能跟被测 SDK 集成的成本低一些
  2. 团队掌握 java 的同学多一些,上手成本较低
  3. 选择 Cucumber-JVM 框架是想通过 BDD 的自然语言特性来对 SDK 的复杂打点字段做一些容易理解的描述,及所谓的 “需求及用例”

Cucumber-JVM 特点

首先介绍一下框架的特征及项目定位:

  1. Cucumber 是一款支持 BDD(Behaviour-Driven Development)而编写的框架,它的支撑语言称作 Gherkin,能够解析后缀名为.feature 和.txt 的文本文件。
  2. Cucumber 开发语言是 ruby,能够运行 ruby,java,.NET,python 等多种语言的脚本。
  3. Cucumber 的文本描述目前支持英、法、中文、日语等 30 多种语言。
  4. Cucumber 可以和主流的测试框架很好地集成,常见的 TestNG、Selenium、SpringFramework、Ruby on Rails 等,能够方便地引入到您的测试工作中去,几乎没有任何门槛。
  5. 我们的 SDK 项目作为 Java 测试项目,因此使用 Cucumber 的 Java 语言实现版本:Cucumber-JVM

Cucumber-JVM 关键字

如上面所说,Cucumber 的核心语法是 Gherkin 提供支持的,每个不为空的行必须以 Gherkin 自带关键字开头, 然后跟随有任意的文本。主要的关键字有:

  • Feature / 功能
  • Scenario / 场景
  • Given, When, Then, And, But (Steps/步骤)
  • Background / 背景
  • Scenario Outline / 场景大纲
  • Examples / 示例

还有其他一些额外的关键字:

  • """ (文档描述)
  • | (表格参数分隔符)
  • @ (Tags 标签)
  • # (注释)
Feature / 功能

"*.feature" 文件通常用来存放一批用例的集合,.feature 文件中存放着大量的测试用例,即 scenarios。
feature 有三个基本元素:

  • Feature: 关键字
  • name: 名称, 在同一行
  • description:描述, 可选(但是强烈推荐),可以占据多行

如果你需要对当前的用例集合做一个详细的解释,可以直接这样写:

Feature: this is a feature 
  #这里是feature文件的详细描述
  I am a description, blablablablablabla.........

  Scenario: just a demo
     Given the cow weighs 450 kg
     When we calculate the feeding requirements

原因是 Cucumber-JVM 不会触发非保留关键字开头的行对应的 step, 所以在 feature 文件开头建议有一个详细的用例集合描述,这是个好习惯。

Scenario / 场景

Scenario 其实就是具体的用例,描述一个用例的 summary(标题)。它由若干步骤组成。
用例可以有任意多个步骤,但是推荐数量保持在每个场景 3-5 个步骤。如果太长,他们将丧失作为规范和文档的表单能力。
场景遵循同样的模式:

  • 描述一个初始化上下文
  • 描述一个过程
  • 描述一个期望的产出
Given / 假设

Given 步骤用于描述用例的一些前提,包括数据准备、初始化环境等等都可以放在这里来写。当然可以有多个 Given 步骤(推荐使用 And 或者 But 来变的更可读)

When / 当

When 步骤用来描述一个事件, 或者一个动作。如果是连续的动作,推荐使用 And 提高可读性。通常我们的用例都会有 When 步骤表示各种操作行为。

Then / 那么

Then 步骤用于描述期望的产出,或者结果。一般断言校验都在这个步骤里体现。

Background / 背景

整个 feature 文件中的所有用例在执行之前都会执行一次 Background 中描述的动作,Background 中的步骤一般没有 Then,用例做一些通用的初始化行为,比如 “清空浏览器缓存”,“卸载之前安装的 APP” 等等。

Scenario Outline / 场景大纲

当有一种用例场景,存在大量重复性的步骤描述,变化的只是输入和输出的时候,非常适合用这个场景大纲结构,举个例子:

Scenario Outline: 这是一个登录的例子
  Given 打开 <page> 登录页面
  When 输入账户名 <username>
  And 输入密码 <password>,点击登录
  Then 成功进入 <result> 页面

  Examples:
    |   page| username| password|result   |
    |    奇虎|  test1  |   123456|奇虎欢迎你 |
    |    百度|  test2  |   123456|百度欢迎你 |
    |    网易|  test3  |   123456|网易欢迎你 |
    |    京东|  test4  |   123456|京东欢迎你 |

以上用例也比较适合接口校验类的用例,我们的 SDK 就使用到了这种描述。

Cucumber-JVM 几个例子
  • 一个简单的例子(英文版):

  • 一个简单的例子(中文版):

  • 中英文混合版(为什么会用到这种,后面会提到)

    • 中英文关键字对照
步骤实现

Cucumber-JVM 描述的每一个场景(用例),背后都需要一个对于的 step 步骤来实现这个描述,Cucumber-JVM 还不能做到文字自动生成代码的地步,目前很多关键字驱动的框架在做类似的事情。
当 Cucumber-JVM 在解析和执行 feature 文件时,它将通过正则匹配的方式寻找代码实现的 steps。比如这样的:

# 用例描述
Scenario: This is a testcase
  Given I have 10 dollor in my wallet

对应的 step 实现为:

@Given("I have (\\d+) dollor in my wallet")
public void I_have_dollor_in_my_wallet(int money){
    System.out.println("money: %n\n", money);
}

细心的读者应该发现上面有参数传递,那么 feature 文件是通过什么样的方式把参数传给 step 的呢?

  • 整型参数

    # 用例描述
    Scenario: This is a testcase
    Given I have 10 dollor in my wallet
    

    这里可以用 (\d+) 来捕获:

    @Given("I have (\\d+) dollor in my wallet")
    public void I_have_dollor_in_my_wallet(int money){
    }
    
  • 字符串参数

    # 用例描述
    Scenario: This is a testcase
    Given I have "10" dollor in my wallet
    

    这里可以用\"([\"]*)\" 来捕获(注意转义符):

    @Given("I have \"([^\"]*)\" dollor in my wallet")
    public void I_have_dollor_in_my_wallet(String money){
    }
    
  • 布尔类型参数
    这种参数通常用在 Then 步骤里作为校验结果。

    # 用例描述
    Scenario: This is a testcase
    Then 返回检查结果为 false
    

    这里可以用\"([\"]*)\" 来捕获(注意转义符):

    @Then("^返回检查结果为 (true|false)$")
    public void verify(boolean expected) {
    }
    
  • 一维数组参数

    • 简单的方式是使用逗号: powershell # 用例描述 Scenario: This is a testcase Given the following animals: cow, horse, sheep 将参数定义为 List: powershell @Given("the following animals: (.*)") public void the_following_animals(List<String> animals) { }
    • 使用 Data Table powershell # 用例描述 Scenario: This is a testcase Given the following animals: | cow | | horse | | sheep | 将参数定义为 List: powershell @Given("the following animals: (.*)") public void the_following_animals(List<String> animals) { } ##### Tags(标签)的妙用 Cucumber 的标签非常好用,举个应用场景的例子,我们的测试用例会有几种维度,比如
  • 按广告平台分,例如:360,google,gdt

  • 按手机厂商分,例如:apple,xiaomi,huawei

  • 按广告类型分,例如:native,video

我们先给测试用例做了标签分类,可以这样写:

@360
Feature: this is a feature 
  #这里是feature文件的详细描述
  I am a description, blablablablablabla.........

  @native
  Scenario: just a demo
    Given the cow weighs 450 kg
    When we calculate the feeding requirements

  @video
  Scenario: just a demo
    Given the cow weighs 450 kg
    When we calculate the feeding requirements

  @xiaomi @native
  Scenario: just a demo
    Given the cow weighs 450 kg
    When we calculate the feeding requirements

假如我这次只想运行原生广告的用例,我可以在运行命令中这样写:

java cucumber.api.cli.Main --tags @native your_features

假如我这次只想运行除了原生广告的用例,我可以在运行命令中这样写:

java cucumber.api.cli.Main --tags ~@native your_features

各种组合情况,想怎么用就怎么用,我们在项目中针对不同 case 的归回可以灵活指定测试范围。

按照行号执行测试用例

命令:java cucumber.api.cli.Main E:/codes/cucumber/demo/src/test/features/第一个例子.feature:12:21可以运行 FixedAmountWithdraw.feature 文件中的第 12 和 21 行,如果需要运行更多的行,只需要在 feature 文件名之后加上 “:行号”。

按照场景名称执行测试用例

命令:java cucumber.api.cli.Main --name 这是一个原生广告请求有table的例子 features可以运行名称为 “这是一个原生广告请求有 table 的例子” 的 Scenario 或者 Scenario Outline。对于跑单个失败的场景时非常有用。

以上命令行只是例子,缺少本地 classpath 引入,强烈建议使用 maven 或者 IDEA 来运行,避免因环境问题导致的错误

Cucumber-JVM 的安装和案例

补充:请确认本地有 Android SDK、JDK1.8、Nodejs、NPM 环境

方法一: Maven 创建 Cucumber-JVM 项目

步骤一:初始化环境

  • 配置 Maven 环境(略过)
  • 创建个目录
  • 执行 mvn archetype:generate 目录
  • 提示输入 maven Choose a number or apply filter 的时候直接回车
  • 注意:groupId 和你的 package 相关,比如这里输入 com.qtest, 而 artifactId 和项目名称相关,比如我这里叫 appiumDemo

步骤二:通过 maven 的 pom 在线安装各种依赖
Maven 本地安装 Cucumber-jvm,需要手动添加很多依赖的 jar 包,利用 maven 远程仓库一键安装完成,在创建的 maven 项目中找到 pom.xml,比如增加如下的节点:

步骤三:直接执行 feature 文件

  1. 直接执行 feature 文件
  2. 把提示的代码复制到 step 文件中 powershell #运行Cucumber-jvm需要用到的命令行: #简单命令: java -cp "./jars/*;." cucumber.api.cli.Main -d path/features/test.feature #命令窗口颜色: java -cp "./jars/*;." cucumber.api.cli.Main -p pretty features -g step_definitions #生成测试报告: java -cp "./jars/*;." cucumber.api.cli.Main -p html:output features -g step_definitions

步骤四:扩展 step 伪代码

  • 在定义的 JAVA 类中填充操作步骤,比如我们项目的 step 实现是类似这样的:

步骤五:执行 feature 文件生成测试报告
Cucumber-JVM 的测试报告比较简陋,后面部分会介绍 Jenkins Cucumber 插件生成美观的报告,生成的结果如下:

方法二:通过 IDEA 创建 Cucumber-JVM 项目

IDEA 创建 Cucumber-JVM 项目

  • 安装 IDEA
  • 配置 JAVA 环境

  • 配置 Maven 依赖

  • 通过 IDEA 自动导入依赖

  • 安装 Cucumber-JVM 的 IDEA 插件

  • 新建一个 feature 文件,可以看到关键字已经高亮

  • 创建 step definition(一定要注意项目的层级目录)

  • 运行 feature 文件的 configuration

以上为第一节的全部内容,主要是介绍了 Cucumber(-JVM)框架的用法。BDD 框架的好处在于至少两个方面:

  • 可执行性(Executable):从技术人员的视角来看,可以像执行代码(Java、Ruby...)一样运行这些用例,来验证、验收目标系统。
  • 规范性(Specification):从非技术人员的视角触发,相比验证本身,他们更加关心系统功能的清晰描述:系统在什么场景下能够做什么样的事情。

参考资料:
https://skyao.gitbooks.io/learning-cucumber/content/practice/pass_parameters.html
https://docs.cucumber.io/

共收到 2 条回复 时间 点赞

直接用 karate 更好

Given,When,Then 我觉得用英文的挺好的,用中文怪怪的(也可能是我用惯了 jbehave),另外 cucumber4 里面 Cucumber Expressions 已经很成熟了,传参的时候用起来比正则表达式好写多了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册