1. 概述

本文将深入讲解 Cucumber 中的 Background(背景) 机制。这是一个非常实用的功能,允许我们在每个测试场景(Scenario)执行前,自动运行一组预设的 Gherkin 语句。它的核心价值在于:减少重复、提升可读性、让测试更聚焦业务逻辑

如果你经常写 Cucumber 测试,肯定遇到过这样的“踩坑”时刻:每个 Scenario 开头都得重复写一堆 Given 来准备测试数据,既啰嗦又难维护。Background 就是来解决这个问题的。

2. Cucumber Background 是什么

简单粗暴地说,Background 就是 Feature 级别的前置条件。它定义的步骤会在该 Feature 下的每一个 Scenario 之前自动执行。

为什么需要它?

我们以一个图书商店应用为例来说明。

先看核心业务类 BookStore

public class BookStore {
    private List<Book> books = new ArrayList<>();
    
    public void addBook(Book book) {
        books.add(book);
    }
    
    public List<Book> booksByAuthor(String author) {
        return books.stream()
          .filter(book -> Objects.equals(author, book.getAuthor()))
          .collect(Collectors.toList());
    }

    public Optional<Book> bookByTitle(String title) {
        return books.stream()
          .filter(book -> book.getTitle().equals(title))
          .findFirst();
    }
}

再看对应的 Step Definitions:

public class BookStoreRunSteps {
    private BookStore store;
    private List<Book> foundBooks;
    private Book foundBook;
    
    @Before
    public void setUp() {
        store = new BookStore();
        foundBooks = new ArrayList<>();
    }
    
    @Given("^I have the following books in the store$")
    public void haveBooksInTheStore(DataTable table) {
        List<List<String>> rows = table.asLists(String.class);

        for (List<String> columns: rows) {
            store.addBook(new Book(columns.get(0), columns.get(1)));
        }
    }
    
    @When("^I search for books by author (.+)$")
    public void searchForBooksByAuthor(String author) {
        foundBooks = store.booksByAuthor(author);
    }

    @When("^I search for a book titled (.+)$")
    public void searchForBookByTitle(String title) {
        foundBook = store.bookByTitle(title).orElse(null);
    }
    
    @Then("^I find (\\d+) books$")
    public void findBooks(int count) {
        assertEquals(count, foundBooks.size());
    }

    @Then("^I find a book$")
    public void findABook() {
        assertNotNull(foundBook);
    }

    @Then("^I find no book$")
    public void findNoBook() {
        assertNull(foundBook);
    }
}

有了这些,我们就可以写 Feature 文件了。但如果没有 Background,会是这样:

Feature: Book Store Without Background
  Scenario: Find books by author
    Given I have the following books in the store
      | The Devil in the White City          | Erik Larson |
      | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
      | In the Garden of Beasts              | Erik Larson |
    When I search for books by author Erik Larson
    Then I find 2 books

  Scenario: Find books by author, but isn't there
    Given I have the following books in the store
      | The Devil in the White City          | Erik Larson |
      | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
      | In the Garden of Beasts              | Erik Larson |
    When I search for books by author Marcel Proust
    Then I find 0 books

  Scenario: Find book by title
    Given I have the following books in the store
      | The Devil in the White City          | Erik Larson |
      | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
      | In the Garden of Beasts              | Erik Larson |
    When I search for a book titled The Lion, the Witch and the Wardrobe
    Then I find a book

  Scenario: Find book by title, but isn't there
    Given I have the following books in the store
      | The Devil in the White City          | Erik Larson |
      | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
      | In the Garden of Beasts              | Erik Larson |
    When I search for a book titled Swann's Way
    Then I find no book

问题很明显

  • 数据初始化重复了 4 次
  • 如果要加一本新书,得改 4 个地方
  • 测试逻辑被大量 setup 步骤淹没,不够清晰

这就是 Background 的用武之地。

3. 实际应用示例

使用 Background 的方式非常简单:在 Feature 文件中,使用 Background 关键字定义一个“背景”块,放在所有 Scenario 之前。

Feature: Book Store With Background
  Background: The Book Store
    Given I have the following books in the store
      | The Devil in the White City          | Erik Larson |
      | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
      | In the Garden of Beasts              | Erik Larson |

  Scenario: Find books by author
    When I search for books by author Erik Larson
    Then I find 2 books

  Scenario: Find books by author, but isn't there
    When I search for books by author Marcel Proust
    Then I find 0 books

  Scenario: Find book by title
    When I search for a book titled The Lion, the Witch and the Wardrobe
    Then I find a book

  Scenario: Find book by title, but isn't there
    When I search for a book titled Swann's Way
    Then I find no book

优化效果立竿见影

  • 每个 Scenario 只保留核心测试逻辑
  • 数据初始化集中管理,一处修改,全局生效
  • Feature 文件可读性大幅提升,业务意图一目了然

⚠️ 注意:Background 中的步骤会在每一个 Scenario 之前执行。如果某个 Scenario 不需要这些前置数据,那它可能不适合放在这个 Feature 里,或者需要重新设计。

4. Background 与 @Before Hook 的区别

这是个高频面试题,也是实际开发中容易混淆的点。

对比项 Cucumber Background @Before Hook
定义位置 Feature 文件(.feature Java 代码(Step Class)
可见性 ✅ 非技术人员(如产品经理)也能看到 ❌ 隐藏在代码中,不可见
维护性 ✅ 修改测试数据无需动代码 ❌ 需要改 Java 代码
执行时机 在每个 Scenario 的 Given 之前 在每个 Scenario 开始前(由 Cucumber 执行)
适用场景 业务相关的前置条件(如初始化测试数据) 技术相关的初始化(如数据库连接、Mock 设置)

📌 核心结论

  • 用 Background 来表达“业务上下文”,比如“用户已登录”、“购物车中有商品”。
  • 用 @Before 来处理“技术细节”,比如重置测试对象、初始化服务依赖。

简单说:让业务逻辑可见,让技术细节隐藏

5. 总结

  • Background 是提升 Cucumber 测试可维护性和可读性的利器,尤其适合处理重复的 Given 步骤。
  • ✅ 它让测试的业务逻辑更加聚焦,减少样板代码。
  • ✅ 与 @Before 相比,最大的优势是透明性——所有 Stakeholder 都能看到测试的前置条件。
  • ⚠️ 合理使用,避免滥用。如果 Background 过于复杂,可能意味着这个 Feature 职责过重,需要拆分。

示例代码已托管至 GitHub:https://github.com/baeldung/testing-examples/tree/main/cucumber-background-demo


原始标题:Cucumber Background | Baeldung