Java 使用 Cucumber-JVM

更新于 2025-12-30

Automation Panda 2017-10-24

本文是对 Cucumber-JVM for Java 的简明而全面的概述。它既是一篇入门指南,也是一份参考手册。如果你还不熟悉 BDD(行为驱动开发),请先学习相关知识再使用 Cucumber-JVM。


简介

Cucumber 是一个用于行为驱动开发(BDD)的开源软件测试自动化框架。它使用一种业务可读、领域特定的语言——Gherkin,来描述功能行为,并将其转化为自动化测试用例。

Cucumber 项目始于 2008 年,由 Aslak Hellesøy 发布了第一个 Ruby 版本。
Cucumber-JVM 是 Cucumber 官方为 Java 提供的移植版本。在 Cucumber-JVM 中,每一个 Gherkin 步骤(step)都会“绑定”到一个执行该步骤的 Java 方法(称为 step definition)。这种绑定通过注解和正则表达式实现。

Cucumber-JVM 能与各种测试工具良好集成。只要是 Java 能做到的事情,Cucumber-JVM 都能处理。它特别适合用于黑盒测试、高于单元级别的功能测试

更新(2018年7月29日):从 3.0.0 版本起,Cucumber-JVM 不再支持除 Java 以外的 JVM 语言(如 Groovy、Scala、Clojure 和 Gosu)。详情请参阅官方 Cucumber 博客文章《Cucumber-JVM is dropping support of JVM Languages》。此外,Gherkin 解析器现在改用 Go 语言编写,而非 Java。


示例项目

GitHub 上提供了两个示例项目,用于配合本文学习:

这些项目使用了 Java、Apache Maven、Selenium WebDriver 和 AssertJ。README 文件中还包含练习题。


先决技能

要成功使用 Cucumber-JVM for Java,你需要掌握以下技能:

  • BDD 和 Gherkin(推荐阅读 BDD 101
  • 中级 Java 编程能力
  • 测试自动化经验(如 JUnit、TestNG)
  • 构建工具使用经验(如 Ant、Maven、Gradle)

必备工具

测试机器必须安装 Java 开发工具包(JDK),以便构建和运行 Cucumber-JVM 测试。同时建议安装所需的构建工具(如 Apache Maven),它会通过依赖管理自动下载 Cucumber-JVM 包。

推荐使用以下 IDE 进行 Cucumber-JVM 自动化开发:

  • JetBrains IntelliJ IDEA(需安装 Cucumber for Java 插件)
  • Eclipse(需安装 Cucumber JVM Eclipse Plugin

强烈建议使用 Git 等软件配置管理(SCM)工具进行版本控制。


版本说明

  • Cucumber-JVM 2.0 于 2017 年 8 月发布,新项目应使用此版本或更高版本
  • 所有新版 Cucumber-JVM 包均位于 Maven Group ID io.cucumber 下。
  • 旧版 Cucumber-JVM 1.x 的包位于 info.cukes 下。

构建管理

Apache Maven 是 Cucumber-JVM 项目的首选构建工具。所有 Cucumber-JVM 包都托管在 Maven Central Repository 上,Maven 可在构建过程中自动运行 Cucumber 测试。

项目应遵循 Maven 标准目录结构(Standard Directory Layout)。示例项目均使用 Maven。虽然 Gradle 也可使用,但需要额外配置。

每个 Maven 项目都有一个 POM 文件用于配置。POM 应包含适当的 Cucumber-JVM 依赖项。由于 Cucumber-JVM 是测试框架,其依赖应使用 test 作用域。

提示:不同 JVM 语言、依赖注入框架和底层测试运行器(如 JUnit)都有对应的独立包。请访问 Maven Central - io.cucumber 获取最新包和版本信息。


项目结构

Cucumber-JVM 测试自动化采用与其他 BDD 框架相同的分层架构:

BDD Automation Layers.png
  • 上层:侧重于规范描述(如 Gherkin 特性文件)
  • 下层:侧重于技术实现(如步骤定义、页面对象)

Cucumber-JVM 测试可以放在产品代码项目中,也可以单独成项目。无论哪种方式,都应遵循 Maven 标准目录结构:测试代码应放在 src/test 目录下


Gherkin 特性文件(Feature Files)

Gherkin 特性文件是包含行为场景的文本文件,扩展名为 .feature。在 Maven 项目中,它们应放在 src/test/resources 目录下(因为它们不是 Java 源文件),并按合理的包结构组织。

建议参考其他 BDD 文档学习如何编写高质量的 Gherkin。


步骤定义类(Step Definition Classes)

步骤定义类是包含实现 Gherkin 步骤方法的 Java 类。它们和普通 Java 类一样,可以包含变量、构造函数和方法。步骤通过正则表达式 + 注解绑定到方法。

  • 特性文件中的任何步骤都可以调用项目中任意步骤定义类中的方法。
  • 在 Maven 项目中,步骤定义类应放在 src/test/java 下的包中,类名建议以 Steps 结尾。

基础示例(传统注解风格)

以下来自 cucumber-jvm-java-example 项目,使用 cucumber-java 包的传统注解方式。每个方法应声明抛出 Throwable,以便异常能被 Cucumber-JVM 捕获。

package com.automationpanda.example.stepdefs;

import com.automationpanda.example.pages.GooglePage;
import cucumber.api.java.After;
import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import static org.assertj.core.api.Assertions.assertThat;

public class GoogleSearchSteps {

  private WebDriver driver;
  private GooglePage googlePage;

  @Before(value = "@web", order = 1)
  public void initWebDriver() throws Throwable {
    driver = new ChromeDriver();
  }

  @Before(value = "@google", order = 10)
  public void initGooglePage() throws Throwable {
    googlePage = new GooglePage(driver);
  }

  @Given("^a web browser is on the Google page$")
  public void aWebBrowserIsOnTheGooglePage() throws Throwable {
    googlePage.navigateToHomePage();
  }

  @When("^the search phrase \"([^\"]*)\" is entered$")
  public void theSearchPhraseIsEntered(String phrase) throws Throwable {
    googlePage.enterSearchPhrase(phrase);
  }

  @Then("^results for \"([^\"]*)\" are shown$")
  public void resultsForAreShown(String phrase) throws Throwable {
    assertThat(googlePage.pageTitleContains(phrase)).isTrue();
  }

  @After(value = "@web")
  public void disposeWebDriver() throws Throwable {
    driver.quit();
  }
}

Java 8 Lambda 风格(更简洁)

在 Java 8 中,可使用 Lambda 表达式编写步骤定义(需 cucumber-java8 包):

package com.automationpanda.example.stepdefs;

import com.automationpanda.example.pages.GooglePage;
import cucumber.api.Scenario;
import cucumber.api.java8.En;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import static org.assertj.core.api.Assertions.assertThat;

public class GoogleSearchSteps implements En {

  private WebDriver driver;
  private GooglePage googlePage;

  // 注意:使用 WebDriver 的钩子超时应设为 0

  public GoogleSearchSteps() {
    Before(new String[]{"@web"}, 0, 1, (Scenario scenario) -> {
      driver = new ChromeDriver();
    });
    Before(new String[]{"@google"}, 0, 10, (Scenario scenario) -> {
      googlePage = new GooglePage(driver);
    });
    Given("^a web browser is on the Google page$", () -> {
      googlePage.navigateToHomePage();
    });
    When("^the search phrase \"([^\"]*)\" is entered$", (String phrase) -> {
      googlePage.enterSearchPhrase(phrase);
    });
    Then("^results for \"([^\"]*)\" are shown$", (String phrase) -> {
      assertThat(googlePage.pageTitleContains(phrase)).isTrue();
    });
    After(new String[]{"@web"}, (Scenario scenario) -> {
      driver.quit();
    });
  }
}

提示:IDE(如 IntelliJ)支持自动生成步骤定义的 stub(桩代码)。

最佳实践:避免使用类继承。父类中的步骤绑定会导致运行时抛出 DuplicateStepDefinitionException。任何通过继承实现的关注点,都可通过其他设计模式(如组合)更好地解决。构造函数主要用于依赖注入,初始化操作应放在 Before 钩子中。


钩子(Hooks)

有些自动化任务(如初始化 WebDriver)不适合写在 Gherkin 中。Cucumber-JVM 提供 BeforeAfter 钩子,分别在场景执行前和后运行,类似于 JUnit 的 @Before / @After

  • 钩子可指定标签(tags)和执行顺序(order)。
  • After 钩子即使在场景失败时也会执行,非常适合做清理工作

示例:

@Before(value = "@web", order = 1)
public void initWebDriver() throws Throwable {
  driver = new ChromeDriver();
}

@Before(value = "@google", order = 10)
public void initGooglePage() throws Throwable {
  googlePage = new GooglePage(driver);
}

@After(value = "@web")
public void disposeWebDriver() throws Throwable {
  driver.quit();
}

注意:Cucumber-JVM 没有提供整个测试套件级别的钩子(如 @BeforeAll)。这保证了测试独立性,但也使全局初始化变得困难。
解决方案:使用单例模式 + 懒加载。详见 Cucumber-JVM Global Hook Workarounds


依赖注入(Dependency Injection)

Cucumber-JVM 支持依赖注入(DI),用于在多个步骤定义类之间共享对象(如 WebDriver 实例)。

  • 不要使用静态变量共享状态——这会破坏测试独立性和并行执行能力。
  • Cucumber-JVM 支持多种 DI 框架(如 PicoContainer、Spring、Guice),每种都有对应的依赖包。

推荐:PicoContainer(最简单)

  • 无需额外配置。
  • 对象通过构造函数参数注入。
  • 同一类型对象在所有需要它的步骤类中是同一个实例(但每个场景会创建新实例)。

示例:

public class WebDriverHolder {
  private WebDriver driver;
  public WebDriver getDriver() { return driver; }
  public void initWebDriver() { driver = new ChromeDriver(); }
}

public class GoogleSearchSteps {
  private WebDriverHolder holder;
  
  public GoogleSearchSteps(WebDriverHolder holder) {
    this.holder = holder;
  }
  
  @Before
  public void initWebDriver() throws Throwable {
    if (holder.getDriver() == null)
      holder.initWebDriver();
  }
}

提示:需要构造参数的对象,可通过 Holder 类或缓存类封装。


自动化支持类(Automation Support Classes)

支持类是 Cucumber-JVM 框架之外、用于实现自动化逻辑的辅助类。它们可以来自:

  • 当前测试项目
  • 私有工具包
  • 开源库

步骤定义应尽量简短,复杂逻辑应委托给支持类,以提高复用性。

常用开源支持库:

  • Selenium WebDriver:Web 浏览器交互
  • REST Assured:REST API 测试
  • AssertJ:流式断言
  • SLF4J / Logback / log4j2:日志记录
  • Extent Reports:测试报告

页面对象(Page Objects)、文件读取器、数据处理器等也都属于支持类。


配置文件(Configuration Files)

配置文件用于提供环境相关数据,如:

  • URL、用户名、密码
  • 日志/报告设置
  • 数据库连接信息

建议格式:CSV、XML、JSON、Java Properties(避免 Excel 等二进制格式,不利于版本控制和 diff)。

  • 配置文件应放在预定义位置,或通过环境变量/系统属性传入路径。
  • 在测试套件启动时一次性加载(通过全局钩子变通方案)。
  • 切勿将配置硬编码到代码中

运行测试

1. 使用 JUnit 或 TestNG

通过 cucumber-junitcucumber-testng 包,结合测试运行器类(Runner Class)执行:

package com.automationpanda.example.runners;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
  plugin = {"pretty", "html:target/cucumber", "junit:target/cucumber.xml"},
  features = "src/test/resources/com/automationpanda/example/features",
  glue = {"com.automationpanda.example.stepdefs"})
public class PandaCucumberTest {
}
  • Maven 会自动运行 *Test.java(测试阶段)和 *IT.java(集成测试阶段)。
  • 使用 mvn clean 清理旧结果。
  • 使用 标签(tags) 避免重复执行相同测试。

2. 使用命令行运行器

Cucumber-JVM 提供 CLI 工具:

java cucumber.api.cli.Main

--help 查看所有选项。

3. 使用 IDE

  • IntelliJ IDEA(安装 Cucumber for Java 插件)
  • Eclipse(安装 Cucumber JVM Eclipse Plugin)

支持:步骤跳转、桩代码生成、灵活运行选项。


Cucumber 选项(Cucumber Options)

可通过以下方式设置:

  • Runner 类中的 @CucumberOptions
  • 命令行系统属性-Dcucumber.options="..."

最实用的选项是 --tags,用于动态选择要运行的场景。

Cucumber-JVM 2.0+ 的标签表达式语法(类英语布尔表达式):

@automated and @web
@web or @service
not @manual
(@web or @service) and (not @wip)

旧版本使用 ~@manual, @web 等复杂语法,现已弃用。


并行执行(Parallel Execution)

并行执行可大幅缩短总测试时间,但需注意:

  • 需要多台机器或线程
  • 需要避免测试间资源冲突(如数据库、文件)
  • 测试需设计为无状态、独立

从 Cucumber-JVM 4.0.0 起,原生支持并行执行
旧版本通常借助 Maven 插件(如 Cucumber-JVM Parallel Plugin 或 Trivago 的 Cucable)实现。