tessl install tessl/maven-io-cucumber--cucumber-expressions@19.0.0Cucumber Expressions are simple patterns for matching Step Definitions with Gherkin steps
This document provides complete, production-ready integration examples for common use cases.
Complete step definition engine for BDD testing.
import io.cucumber.cucumberexpressions.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class StepDefinitionEngine {
private final ParameterTypeRegistry registry;
private final ExpressionFactory factory;
private final Map<Expression, Method> stepDefinitions = new ConcurrentHashMap<>();
public StepDefinitionEngine(Locale locale) {
this.registry = new ParameterTypeRegistry(locale);
registerCustomTypes();
this.factory = new ExpressionFactory(registry);
}
private void registerCustomTypes() {
// Register domain-specific types
registry.defineParameterType(new ParameterType<>(
"user",
"[A-Za-z][A-Za-z0-9_]*",
String.class,
s -> s
));
registry.defineParameterType(new ParameterType<>(
"email",
"[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}",
String.class,
s -> s
));
// Register enums
registry.defineParameterType(ParameterType.fromEnum(Status.class));
}
public void registerStepDefinition(String pattern, Method method) {
Expression expr = factory.createExpression(pattern);
stepDefinitions.put(expr, method);
}
public void executeStep(String stepText, Object testInstance) throws Exception {
for (Map.Entry<Expression, Method> entry : stepDefinitions.entrySet()) {
Optional<List<Argument<?>>> match = entry.getKey().match(stepText);
if (match.isPresent()) {
Method method = entry.getValue();
Object[] args = extractArguments(match.get(), method);
method.invoke(testInstance, args);
return;
}
}
throw new IllegalArgumentException("No matching step definition for: " + stepText);
}
private Object[] extractArguments(List<Argument<?>> arguments, Method method) {
Object[] args = new Object[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
args[i] = arguments.get(i).getValue();
}
return args;
}
}
// Usage in test class
enum Status { ACTIVE, INACTIVE, PENDING }
public class UserSteps {
private final StepDefinitionEngine engine;
private final Map<String, User> users = new HashMap<>();
public UserSteps() {
this.engine = new StepDefinitionEngine(Locale.ENGLISH);
registerSteps();
}
private void registerSteps() throws NoSuchMethodException {
engine.registerStepDefinition(
"User {user} registers with email {email}",
UserSteps.class.getMethod("userRegisters", String.class, String.class)
);
engine.registerStepDefinition(
"User {user} is {Status}",
UserSteps.class.getMethod("userHasStatus", String.class, Status.class)
);
}
public void userRegisters(String username, String email) {
users.put(username, new User(username, email));
System.out.println("Registered user: " + username + " with email: " + email);
}
public void userHasStatus(String username, Status status) {
User user = users.get(username);
user.setStatus(status);
System.out.println("User " + username + " status: " + status);
}
public void runTest() throws Exception {
engine.executeStep("User john registers with email john@example.com", this);
engine.executeStep("User john is ACTIVE", this);
}
}Validate REST API requests using expressions.
import io.cucumber.cucumberexpressions.*;
import java.util.*;
import java.util.regex.Pattern;
public class ApiValidationFramework {
private final ExpressionFactory factory;
public ApiValidationFramework(ParameterTypeRegistry registry) {
registerApiTypes(registry);
this.factory = new ExpressionFactory(registry);
}
private void registerApiTypes(ParameterTypeRegistry registry) {
// HTTP method
registry.defineParameterType(new ParameterType<>(
"method",
"GET|POST|PUT|DELETE|PATCH",
String.class,
s -> s
));
// Status code
registry.defineParameterType(new ParameterType<>(
"status",
"\\d{3}",
Integer.class,
Integer::parseInt
));
// JSON path
registry.defineParameterType(new ParameterType<>(
"jsonpath",
"\\$[\\w.\\[\\]]+",
String.class,
s -> s
));
}
public ValidationResult validate(String rule, Map<String, Object> apiResponse) {
List<String> errors = new ArrayList<>();
try {
Expression expr = factory.createExpression(rule);
// Example: "Response status should be {status}"
if (rule.contains("status")) {
Optional<List<Argument<?>>> match = expr.match(rule);
if (match.isPresent()) {
Integer expectedStatus = (Integer) match.get().get(0).getValue();
Integer actualStatus = (Integer) apiResponse.get("status");
if (!expectedStatus.equals(actualStatus)) {
errors.add("Expected status " + expectedStatus +
" but got " + actualStatus);
}
}
}
return new ValidationResult(errors.isEmpty(), errors);
} catch (Exception e) {
errors.add("Validation error: " + e.getMessage());
return new ValidationResult(false, errors);
}
}
public static class ValidationResult {
private final boolean valid;
private final List<String> errors;
public ValidationResult(boolean valid, List<String> errors) {
this.valid = valid;
this.errors = errors;
}
public boolean isValid() { return valid; }
public List<String> getErrors() { return errors; }
}
}
// Usage
ParameterTypeRegistry registry = new ParameterTypeRegistry(Locale.ENGLISH);
ApiValidationFramework validator = new ApiValidationFramework(registry);
Map<String, Object> response = new HashMap<>();
response.put("status", 200);
response.put("body", "{\"user\":\"john\"}");
ApiValidationFramework.ValidationResult result = validator.validate(
"Response status should be {status}",
response
);
if (result.isValid()) {
System.out.println("API validation passed");
} else {
result.getErrors().forEach(System.err::println);
}Parse structured logs using cucumber expressions.
import io.cucumber.cucumberexpressions.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
public class LogParser {
private final ExpressionFactory factory;
private final Map<String, Expression> patterns = new LinkedHashMap<>();
public LogParser(ParameterTypeRegistry registry) {
registerLogTypes(registry);
this.factory = new ExpressionFactory(registry);
initializePatterns();
}
private void registerLogTypes(ParameterTypeRegistry registry) {
// Timestamp
registry.defineParameterType(new ParameterType<>(
"timestamp",
"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}",
LocalDateTime.class,
s -> LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
));
// Log level
registry.defineParameterType(new ParameterType<>(
"level",
"DEBUG|INFO|WARN|ERROR|FATAL",
LogLevel.class,
LogLevel::valueOf
));
// Thread ID
registry.defineParameterType(new ParameterType<>(
"thread",
"thread-\\d+",
String.class,
s -> s
));
}
private void initializePatterns() {
patterns.put("standard", factory.createExpression(
"{timestamp} [{level}] ({thread}) - {string}"
));
patterns.put("error", factory.createExpression(
"{timestamp} [{level}] ({thread}) - Error: {string} at {word}"
));
}
public LogEntry parseLine(String logLine) {
for (Map.Entry<String, Expression> entry : patterns.entrySet()) {
Optional<List<Argument<?>>> match = entry.getValue().match(logLine);
if (match.isPresent()) {
return createLogEntry(entry.getKey(), match.get());
}
}
return null;
}
private LogEntry createLogEntry(String pattern, List<Argument<?>> args) {
LogEntry entry = new LogEntry();
entry.setPattern(pattern);
if (pattern.equals("standard")) {
entry.setTimestamp((LocalDateTime) args.get(0).getValue());
entry.setLevel((LogLevel) args.get(1).getValue());
entry.setThread((String) args.get(2).getValue());
entry.setMessage((String) args.get(3).getValue());
} else if (pattern.equals("error")) {
entry.setTimestamp((LocalDateTime) args.get(0).getValue());
entry.setLevel((LogLevel) args.get(1).getValue());
entry.setThread((String) args.get(2).getValue());
entry.setError((String) args.get(3).getValue());
entry.setLocation((String) args.get(4).getValue());
}
return entry;
}
enum LogLevel { DEBUG, INFO, WARN, ERROR, FATAL }
static class LogEntry {
private String pattern;
private LocalDateTime timestamp;
private LogLevel level;
private String thread;
private String message;
private String error;
private String location;
// Getters and setters...
public void setPattern(String pattern) { this.pattern = pattern; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public void setLevel(LogLevel level) { this.level = level; }
public void setThread(String thread) { this.thread = thread; }
public void setMessage(String message) { this.message = message; }
public void setError(String error) { this.error = error; }
public void setLocation(String location) { this.location = location; }
@Override
public String toString() {
return String.format("[%s] %s %s %s",
level, timestamp, thread, message != null ? message : error);
}
}
}
// Usage
ParameterTypeRegistry registry = new ParameterTypeRegistry(Locale.ENGLISH);
LogParser parser = new LogParser(registry);
String logLine = "2026-01-30T14:23:45 [INFO] (thread-1) - \"Application started\"";
LogParser.LogEntry entry = parser.parseLine(logLine);
if (entry != null) {
System.out.println("Parsed log: " + entry);
}Parse CLI commands with cucumber expressions.
import io.cucumber.cucumberexpressions.*;
import java.util.*;
public class CliParser {
private final ExpressionFactory factory;
private final Map<String, CommandHandler> commands = new HashMap<>();
public CliParser(ParameterTypeRegistry registry) {
this.factory = new ExpressionFactory(registry);
}
public void registerCommand(String pattern, CommandHandler handler) {
commands.put(pattern, handler);
}
public CommandResult execute(String commandLine) {
for (Map.Entry<String, CommandHandler> entry : commands.entrySet()) {
String pattern = entry.getKey();
CommandHandler handler = entry.getValue();
try {
Expression expr = factory.createExpression(pattern);
Optional<List<Argument<?>>> match = expr.match(commandLine);
if (match.isPresent()) {
Object[] args = match.get().stream()
.map(Argument::getValue)
.toArray();
String result = handler.execute(args);
return CommandResult.success(result);
}
} catch (Exception e) {
return CommandResult.error(e.getMessage());
}
}
return CommandResult.error("Unknown command: " + commandLine);
}
@FunctionalInterface
public interface CommandHandler {
String execute(Object[] args) throws Exception;
}
public static class CommandResult {
private final boolean success;
private final String output;
private CommandResult(boolean success, String output) {
this.success = success;
this.output = output;
}
public static CommandResult success(String output) {
return new CommandResult(true, output);
}
public static CommandResult error(String error) {
return new CommandResult(false, error);
}
public boolean isSuccess() { return success; }
public String getOutput() { return output; }
}
}
// Usage
ParameterTypeRegistry registry = new ParameterTypeRegistry(Locale.ENGLISH);
CliParser cli = new CliParser(registry);
// Register commands
cli.registerCommand("list users", args -> {
return "Users: [john, jane, bob]";
});
cli.registerCommand("create user {word} with email {word}", args -> {
String username = (String) args[0];
String email = (String) args[1];
return "Created user " + username + " with email " + email;
});
cli.registerCommand("delete user {word}", args -> {
String username = (String) args[0];
return "Deleted user " + username;
});
// Execute commands
CliParser.CommandResult result1 = cli.execute("list users");
System.out.println(result1.getOutput());
CliParser.CommandResult result2 = cli.execute("create user alice with email alice@example.com");
System.out.println(result2.getOutput());Parse configuration files with structured values.
import io.cucumber.cucumberexpressions.*;
import java.util.*;
public class ConfigParser {
private final ExpressionFactory factory;
private final Map<String, Object> config = new HashMap<>();
public ConfigParser(ParameterTypeRegistry registry) {
registerConfigTypes(registry);
this.factory = new ExpressionFactory(registry);
}
private void registerConfigTypes(ParameterTypeRegistry registry) {
// Boolean
registry.defineParameterType(new ParameterType<>(
"bool",
"true|false|yes|no|on|off",
Boolean.class,
s -> s.matches("true|yes|on")
));
// Port number
registry.defineParameterType(new ParameterType<>(
"port",
"\\d{1,5}",
Integer.class,
s -> {
int port = Integer.parseInt(s);
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port: " + port);
}
return port;
}
));
// Path
registry.defineParameterType(new ParameterType<>(
"path",
"[\\w/.-]+",
String.class,
s -> s
));
}
public void parseLine(String line) {
// Remove comments
int commentIndex = line.indexOf('#');
if (commentIndex >= 0) {
line = line.substring(0, commentIndex);
}
line = line.trim();
if (line.isEmpty()) {
return;
}
// Parse different config formats
parseConfigLine(line);
}
private void parseConfigLine(String line) {
Expression[] patterns = {
factory.createExpression("set {word} to {int}"),
factory.createExpression("set {word} to {bool}"),
factory.createExpression("set {word} to {string}"),
factory.createExpression("server port is {port}"),
factory.createExpression("database path is {path}")
};
for (Expression expr : patterns) {
Optional<List<Argument<?>>> match = expr.match(line);
if (match.isPresent()) {
storeConfig(match.get());
return;
}
}
throw new IllegalArgumentException("Invalid config line: " + line);
}
private void storeConfig(List<Argument<?>> args) {
if (args.size() >= 2) {
String key = (String) args.get(0).getValue();
Object value = args.get(1).getValue();
config.put(key, value);
}
}
public Map<String, Object> getConfig() {
return Collections.unmodifiableMap(config);
}
}
// Usage
ParameterTypeRegistry registry = new ParameterTypeRegistry(Locale.ENGLISH);
ConfigParser parser = new ConfigParser(registry);
// Parse config lines
parser.parseLine("set timeout to 30");
parser.parseLine("set debug to true");
parser.parseLine("set name to \"MyApp\"");
parser.parseLine("server port is 8080");
parser.parseLine("database path is /var/lib/data");
// Get config
Map<String, Object> config = parser.getConfig();
config.forEach((key, value) ->
System.out.println(key + " = " + value + " (" + value.getClass().getSimpleName() + ")")
);Parse data migration scripts with complex transformations.
import io.cucumber.cucumberexpressions.*;
import java.util.*;
import java.time.LocalDate;
public class MigrationScriptParser {
private final ExpressionFactory factory;
private final List<MigrationCommand> commands = new ArrayList<>();
public MigrationScriptParser(ParameterTypeRegistry registry) {
registerMigrationTypes(registry);
this.factory = new ExpressionFactory(registry);
}
private void registerMigrationTypes(ParameterTypeRegistry registry) {
// Table name
registry.defineParameterType(new ParameterType<>(
"table",
"[a-z_][a-z0-9_]*",
String.class,
s -> s
));
// Column name
registry.defineParameterType(new ParameterType<>(
"column",
"[a-z_][a-z0-9_]*",
String.class,
s -> s
));
// Data type
registry.defineParameterType(new ParameterType<>(
"datatype",
"VARCHAR|INTEGER|DATE|BOOLEAN|TEXT",
String.class,
s -> s
));
}
public void parseScript(String script) {
String[] lines = script.split("\\n");
for (String line : lines) {
line = line.trim();
if (line.isEmpty() || line.startsWith("--")) {
continue;
}
parseLine(line);
}
}
private void parseLine(String line) {
// CREATE TABLE
Expression createTable = factory.createExpression(
"CREATE TABLE {table}"
);
Optional<List<Argument<?>>> match1 = createTable.match(line);
if (match1.isPresent()) {
String tableName = (String) match1.get().get(0).getValue();
commands.add(new CreateTableCommand(tableName));
return;
}
// ADD COLUMN
Expression addColumn = factory.createExpression(
"ADD COLUMN {column} {datatype} TO {table}"
);
Optional<List<Argument<?>>> match2 = addColumn.match(line);
if (match2.isPresent()) {
String columnName = (String) match2.get().get(0).getValue();
String dataType = (String) match2.get().get(1).getValue();
String tableName = (String) match2.get().get(2).getValue();
commands.add(new AddColumnCommand(tableName, columnName, dataType));
return;
}
// DROP COLUMN
Expression dropColumn = factory.createExpression(
"DROP COLUMN {column} FROM {table}"
);
Optional<List<Argument<?>>> match3 = dropColumn.match(line);
if (match3.isPresent()) {
String columnName = (String) match3.get().get(0).getValue();
String tableName = (String) match3.get().get(1).getValue();
commands.add(new DropColumnCommand(tableName, columnName));
return;
}
}
public List<MigrationCommand> getCommands() {
return Collections.unmodifiableList(commands);
}
// Command classes
interface MigrationCommand {
String toSql();
}
static class CreateTableCommand implements MigrationCommand {
private final String tableName;
CreateTableCommand(String tableName) { this.tableName = tableName; }
public String toSql() { return "CREATE TABLE " + tableName + ";"; }
}
static class AddColumnCommand implements MigrationCommand {
private final String tableName, columnName, dataType;
AddColumnCommand(String tableName, String columnName, String dataType) {
this.tableName = tableName;
this.columnName = columnName;
this.dataType = dataType;
}
public String toSql() {
return "ALTER TABLE " + tableName + " ADD COLUMN " + columnName + " " + dataType + ";";
}
}
static class DropColumnCommand implements MigrationCommand {
private final String tableName, columnName;
DropColumnCommand(String tableName, String columnName) {
this.tableName = tableName;
this.columnName = columnName;
}
public String toSql() {
return "ALTER TABLE " + tableName + " DROP COLUMN " + columnName + ";";
}
}
}
// Usage
ParameterTypeRegistry registry = new ParameterTypeRegistry(Locale.ENGLISH);
MigrationScriptParser parser = new MigrationScriptParser(registry);
String script = """
CREATE TABLE users
ADD COLUMN email VARCHAR TO users
ADD COLUMN age INTEGER TO users
DROP COLUMN old_field FROM users
""";
parser.parseScript(script);
List<MigrationScriptParser.MigrationCommand> commands = parser.getCommands();
commands.forEach(cmd -> System.out.println(cmd.toSql()));