参数类型:让Cucumber参数"活"起来
参数类型可以将Cucumber表达式中的参数自动转换为自定义对象,就像给参数"穿上马甲",让它们变成程序能直接使用的类型。
核心概念
-
参数类型:将表达式参数转换为对象
-
数据表/文档字符串类型:将表格或多行文本转换为对象
-
自动检测:放在胶水代码路径下,Cucumber会自动发现它们
三种自定义类型转换方式
1. 数据表类型(DataTableType) - 表格变对象
将数据表中的每一行转换为自定义对象:
java
package com.example;
import io.cucumber.java.DataTableType;
import io.cucumber.java.en.Given;
import java.util.List;
import java.util.Map;
public class StepDefinitions {
@DataTableType
public Author authorEntry(Map<String, String> entry) {
// 把表格行映射成Author对象
return new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook"));
}
@Given("以下是我最喜欢的作者")
public void these_are_my_favourite_authors(List<Author> authors) {
// 现在authors是Author对象列表,不是原始数据
authors.forEach(author -> System.out.println(author.getName()));
}
}
2. 参数类型(ParameterType) - 表达式参数变对象
将步骤中的参数转换为特定类型:
java
package com.example;
import io.cucumber.java.ParameterType;
import io.cucumber.java.en.Given;
public class StepDefinitions {
@ParameterType(".*") // 匹配任何文本
public Book book(String bookName) {
return new Book(bookName); // 将字符串转换为Book对象
}
@Given("{book}是我最喜欢的书")
public void this_is_my_favorite_book(Book book) {
// book已经是Book对象,可以直接调用方法
System.out.println("我最喜欢的书:" + book.getTitle());
}
}
3. 文档字符串类型(DocStringType) - 多行文本变对象
将多行文本(如JSON、XML)转换为对象:
java
package com.example;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DocStringType;
import io.cucumber.java.en.Given;
public class StepsDefinitions {
private static ObjectMapper objectMapper = new ObjectMapper();
@DocStringType
public JsonNode json(String docString) throws JsonProcessingException {
// 将JSON字符串转换为JsonNode对象
return objectMapper.readValue(docString, JsonNode.class);
}
@Given("书籍信息由JSON定义")
public void books_are_defined_by_json(JsonNode books) {
// books已经是解析好的JSON对象
System.out.println("书籍数量:" + books.size());
}
}
Lambda风格的实现方式
如果你喜欢更简洁的Lambda表达式:
java
package com.example;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java8.En;
import java.util.Map;
public class LambdaStepDefinitions implements En {
private static ObjectMapper objectMapper = new ObjectMapper();
public LambdaStepDefinitions() {
// Lambda风格的类型注册
DataTableType((Map<String, String> entry) -> new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook")
));
ParameterType("book", ".*", (String bookName) -> new Book(bookName));
DocStringType("json", (String docString) ->
objectMapper.readValue(docString, JsonNode.class));
}
}
通用转换器 - 让Jackson等库自动处理
如果不想为每个类型都写转换逻辑,可以使用通用转换器:
java
package com.example;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DefaultDataTableCellTransformer;
import io.cucumber.java.DefaultDataTableEntryTransformer;
import io.cucumber.java.DefaultParameterTransformer;
import java.lang.reflect.Type;
public class StepDefinitions {
private final ObjectMapper objectMapper = new ObjectMapper();
@DefaultParameterTransformer
@DefaultDataTableEntryTransformer
@DefaultDataTableCellTransformer
public Object transformer(Object fromValue, Type toValueType) {
// 让Jackson自动处理所有转换
return objectMapper.convertValue(fromValue,
objectMapper.constructType(toValueType));
}
}
Lambda风格的通用转换器:
java
package com.example;
import io.cucumber.java8.En;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;
public class LambdaStepDefinitions implements En {
public LambdaStepDefinitions() {
ObjectMapper objectMapper = new ObjectMapper();
DefaultParameterTransformer((String fromValue, Type toValueType) ->
objectMapper.convertValue(fromValue,
objectMapper.constructType(toValueType)));
DefaultDataTableCellTransformer((fromValue, toValueType) ->
objectMapper.convertValue(fromValue,
objectMapper.constructType(toValueType)));
DefaultDataTableEntryTransformer((fromValue, toValueType) ->
objectMapper.convertValue(fromValue,
objectMapper.constructType(toValueType)));
}
}
错误提示
如果使用了未定义的类型,会看到清晰的错误信息:
The parameter type "person" is not defined. (参数类型"person"未定义。)
配置:让测试适应不同环境
配置文件(Profiles)- Java版的特殊处理
Cucumber-JVM本身不支持配置文件功能,但可以通过构建工具来实现类似效果。
Maven配置示例:为不同环境设置不同标签
java
<profiles>
<profile>
<id>dev</id> <!-- 开发环境 -->
<properties>
<!-- 只运行@dev标签,跳过@ignore标签 -->
<cucumber.filter.tags>@dev and not @ignore</cucumber.filter.tags>
</properties>
</profile>
<profile>
<id>qa</id> <!-- 测试环境 -->
<properties>
<!-- 只运行@qa标签 -->
<cucumber.filter.tags>@qa</cucumber.filter.tags>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<systemPropertyVariables>
<!-- 将Maven属性传递给Cucumber -->
<cucumber.filter.tags>${cucumber.filter.tags}</cucumber.filter.tags>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
使用方式
bash
# 运行开发环境的测试
mvn test -Pdev
# 运行测试环境的测试
mvn test -Pqa
Gradle的替代方案
Gradle可以通过任务配置来实现类似效果,具体参考Gradle文档中的"Maven profiles and properties迁移"部分。
环境变量
重要说明 :Cucumber-JVM不支持通过.env文件配置环境变量。如果需要设置环境相关配置,可以通过:
-
系统属性(如上例中的Maven配置)
-
操作系统的环境变量
-
配置文件读取
通俗理解:参数类型就像"翻译官"
参数类型的实际意义
想象一下你去国外餐厅点餐:
-
没有参数类型:服务员给你一本全是外文的菜单,你得自己查字典
-
有参数类型:服务员直接给你中文菜单,还帮你推荐菜品
配置文件的实际应用场景
-
开发环境:只想运行自己正在开发的测试,不想跑全部
-
测试环境:QA团队只想运行冒烟测试或回归测试
-
生产前:运行所有测试,确保一切正常
简单记忆口诀
-
参数类型:字符串变对象,测试更轻松
-
数据表类型:表格数据直接转,省去解析麻烦
-
文档字符串:JSON/XML自动解析,拿来就能用
-
配置分离:不同环境不同测试,灵活又方便
实际工作流程
编写特性文件 → Cucumber解析参数 → 自定义类型转换 → 得到业务对象 → 执行测试断言 (Given/When/Then) ↓ ↓ (字符串) → (Author/Book等对象)
这样设计的好处是:测试代码更简洁,业务逻辑更清晰,维护成本更低。