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