CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-cucumber--cucumber-java8

Provides lambda-based step definitions for Cucumber BDD testing framework with Java 8+ functional interfaces

Pending
Overview
Eval results
Files

transformers.mddocs/

Transformers

Comprehensive transformation system for converting between string representations and Java objects. Includes parameter types, data table types, doc string types, and default transformers that integrate with object mappers for automatic conversion.

Capabilities

Parameter Types

Custom parameter types for extracting and transforming step definition parameters from regular expressions, supporting 1-9 capture groups with full type safety.

/**
 * Parameter type definitions with 1-9 parameter support
 * Converts regex capture groups to custom Java objects
 */
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A1<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A2<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A3<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A4<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A5<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A6<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A7<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A8<R> body) { ... }
default <R> void ParameterType(String name, String regexp, ParameterDefinitionBody.A9<R> body) { ... }

Usage Examples:

import io.cucumber.java8.En;
import java.math.BigDecimal;
import java.util.Currency;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class ParameterTypeDefinitions implements En {
    
    public ParameterTypeDefinitions() {
        // Single parameter type - Currency amount
        ParameterType("amount", "(\\d+\\.\\d+)\\s([A-Z]{3})", (String value, String currency) ->
            new Amount(new BigDecimal(value), Currency.getInstance(currency)));
        
        // Multiple parameter type - Date with format
        ParameterType("date", "(\\d{4})-(\\d{2})-(\\d{2})", (String year, String month, String day) ->
            LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day)));
        
        // Complex parameter type - Person with multiple attributes
        ParameterType("person", "([A-Za-z]+)\\s([A-Za-z]+)\\s(\\d+)\\syears\\sold", 
            (String firstName, String lastName, String age) ->
                new Person(firstName, lastName, Integer.parseInt(age)));
        
        // Coordinate parameter type
        ParameterType("coordinate", "\\(([-\\d\\.]+),\\s*([-\\d\\.]+)\\)", 
            (String x, String y) -> new Point(Double.parseDouble(x), Double.parseDouble(y)));
        
        // Color parameter type with validation
        ParameterType("color", "(red|green|blue|yellow|purple|orange)", (String colorName) -> {
            switch (colorName.toLowerCase()) {
                case "red": return Color.RED;
                case "green": return Color.GREEN;
                case "blue": return Color.BLUE;
                case "yellow": return Color.YELLOW;
                case "purple": return Color.PURPLE;
                case "orange": return Color.ORANGE;
                default: throw new IllegalArgumentException("Unknown color: " + colorName);
            }
        });
    }
}

// Usage in step definitions
public class StepDefinitions implements En {
    public StepDefinitions() {
        When("I transfer {amount} to the account", (Amount amount) -> {
            bankingService.transfer(amount);
        });
        
        Then("the transaction date should be {date}", (LocalDate expectedDate) -> {
            assertEquals(expectedDate, transaction.getDate());
        });
        
        Given("a {person} is registered", (Person person) -> {
            userService.register(person);
        });
        
        When("I click at {coordinate}", (Point coordinates) -> {
            mouseService.click(coordinates.getX(), coordinates.getY());
        });
        
        Then("the button should be {color}", (Color expectedColor) -> {
            assertEquals(expectedColor, button.getColor());
        });
    }
}

Parameter Definition Functional Interfaces

Functional interfaces for parameter type transformation lambdas supporting 1-9 parameters.

/**
 * Functional interfaces for parameter type transformations
 * Each interface corresponds to the number of regex capture groups
 */
public interface ParameterDefinitionBody {
    @FunctionalInterface
    interface A1<R> {
        /**
         * Transform single parameter to return type
         * @param arg1 First regex capture group
         * @return Transformed object of type R
         * @throws Throwable Any exception during transformation
         */
        R accept(String arg1) throws Throwable;
    }
    
    @FunctionalInterface
    interface A2<R> {
        /**
         * Transform two parameters to return type
         * @param arg1 First regex capture group
         * @param arg2 Second regex capture group
         * @return Transformed object of type R
         * @throws Throwable Any exception during transformation
         */
        R accept(String arg1, String arg2) throws Throwable;
    }
    
    @FunctionalInterface
    interface A3<R> {
        R accept(String arg1, String arg2, String arg3) throws Throwable;
    }
    
    @FunctionalInterface
    interface A4<R> {
        R accept(String arg1, String arg2, String arg3, String arg4) throws Throwable;
    }
    
    @FunctionalInterface
    interface A5<R> {
        R accept(String arg1, String arg2, String arg3, String arg4, String arg5) throws Throwable;
    }
    
    @FunctionalInterface
    interface A6<R> {
        R accept(String arg1, String arg2, String arg3, String arg4, String arg5, String arg6) throws Throwable;
    }
    
    @FunctionalInterface
    interface A7<R> {
        R accept(String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, String arg7) throws Throwable;
    }
    
    @FunctionalInterface
    interface A8<R> {
        R accept(String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, String arg7, String arg8) throws Throwable;
    }
    
    @FunctionalInterface
    interface A9<R> {
        R accept(String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, String arg7, String arg8, String arg9) throws Throwable;
    }
}

Data Table Types

Transform Gherkin data tables into custom Java objects at different granularities: entire table, entry (row as Map), row (List), or individual cell.

/**
 * Data table type transformations for different table structures
 */
default <T> void DataTableType(DataTableDefinitionBody<T> body) { ... }
default <T> void DataTableType(DataTableEntryDefinitionBody<T> body) { ... }
default <T> void DataTableType(DataTableRowDefinitionBody<T> body) { ... }
default <T> void DataTableType(DataTableCellDefinitionBody<T> body) { ... }

/**
 * Data table types with empty cell replacement
 */
default <T> void DataTableType(String replaceWithEmptyString, DataTableDefinitionBody<T> body) { ... }
default <T> void DataTableType(String replaceWithEmptyString, DataTableEntryDefinitionBody<T> body) { ... }
default <T> void DataTableType(String replaceWithEmptyString, DataTableRowDefinitionBody<T> body) { ... }
default <T> void DataTableType(String replaceWithEmptyString, DataTableCellDefinitionBody<T> body) { ... }

Usage Examples:

public class DataTableTypeDefinitions implements En {
    
    public DataTableTypeDefinitions() {
        // Entry-level transformation (Map<String,String> -> Custom Object)
        DataTableType((Map<String, String> entry) -> new User(
            entry.get("name"),
            entry.get("email"),
            Integer.parseInt(entry.get("age")),
            Boolean.parseBoolean(entry.get("active"))
        ));
        
        // Row-level transformation (List<String> -> Custom Object)
        DataTableType((List<String> row) -> new Product(
            row.get(0),                    // name
            new BigDecimal(row.get(1)),    // price
            Integer.parseInt(row.get(2))   // quantity
        ));
        
        // Cell-level transformation (String -> Custom Object)
        DataTableType((String cell) -> {
            String[] parts = cell.split(":");
            return new KeyValue(parts[0], parts[1]);
        });
        
        // Table-level transformation (entire DataTable -> Custom Object)
        DataTableType((DataTable table) -> {
            List<List<String>> rows = table.asLists();
            return new Matrix(rows);
        });
        
        // Entry transformation with empty cell replacement
        DataTableType("[blank]", (Map<String, String> entry) -> new Author(
            entry.get("name"),
            entry.get("first_publication")  // "[blank]" becomes empty string
        ));
        
        // Complex entry transformation with validation
        DataTableType((Map<String, String> entry) -> {
            validateRequiredFields(entry, "name", "email");
            
            return User.builder()
                .name(entry.get("name"))
                .email(entry.get("email"))
                .age(parseAge(entry.get("age")))
                .roles(parseRoles(entry.get("roles")))
                .createdAt(parseDate(entry.get("created_at")))
                .build();
        });
    }
}

// Usage in step definitions
public class DataTableSteps implements En {
    public DataTableSteps() {
        Given("the following users exist:", (DataTable userTable) -> {
            List<User> users = userTable.asList(User.class);
            userService.createUsers(users);
        });
        
        When("I add these products:", (DataTable productTable) -> {
            List<Product> products = productTable.asList(Product.class);
            inventory.addProducts(products);
        });
        
        Then("the configuration should contain:", (DataTable configTable) -> {
            List<KeyValue> config = configTable.asList(KeyValue.class);
            assertConfigMatches(config);
        });
        
        // Using transposed tables
        Given("the user details:", (DataTable userTable) -> {
            User user = userTable.transpose().asList(User.class).get(0);
            currentUser = user;
        });
    }
}

Data Table Functional Interfaces

Functional interfaces for different levels of data table transformation.

/**
 * Transform entire data table to custom object
 */
@FunctionalInterface
public interface DataTableDefinitionBody<T> {
    /**
     * Transform complete DataTable to custom type
     * @param table Complete data table with headers and rows
     * @return Transformed object of type T
     * @throws Throwable Any exception during transformation
     */
    T accept(DataTable table) throws Throwable;
}

/**
 * Transform data table entry (header-value map) to custom object
 */
@FunctionalInterface
public interface DataTableEntryDefinitionBody<T> {
    /**
     * Transform data table entry to custom type
     * @param entry Map representing one table row with column headers as keys
     * @return Transformed object of type T
     * @throws Throwable Any exception during transformation
     */
    T accept(Map<String, String> entry) throws Throwable;
}

/**
 * Transform data table row (list of values) to custom object
 */
@FunctionalInterface  
public interface DataTableRowDefinitionBody<T> {
    /**
     * Transform data table row to custom type
     * @param row List of string values representing one table row
     * @return Transformed object of type T
     * @throws Throwable Any exception during transformation
     */
    T accept(List<String> row) throws Throwable;
}

/**
 * Transform individual data table cell to custom object
 */
@FunctionalInterface
public interface DataTableCellDefinitionBody<T> {
    /**
     * Transform individual cell value to custom type
     * @param cell String value of a single table cell
     * @return Transformed object of type T  
     * @throws Throwable Any exception during transformation
     */
    T accept(String cell) throws Throwable;
}

Doc String Types

Transform Gherkin doc strings (multi-line text blocks) into custom Java objects with optional content type filtering.

/**
 * Doc string type transformation for multi-line text content
 */
default <T> void DocStringType(String contentType, DocStringDefinitionBody<T> body) { ... }

Usage Examples:

public class DocStringTypeDefinitions implements En {
    
    public DocStringTypeDefinitions() {
        // JSON doc string transformation
        DocStringType("json", (String docString) -> {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.readValue(docString, JsonNode.class);
        });
        
        // XML doc string transformation
        DocStringType("xml", (String docString) -> {
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(new ByteArrayInputStream(docString.getBytes()));
        });
        
        // CSV doc string transformation
        DocStringType("csv", (String docString) -> {
            List<List<String>> rows = new ArrayList<>();
            String[] lines = docString.split("\\n");
            for (String line : lines) {
                rows.add(Arrays.asList(line.split(",")));
            }
            return rows;
        });
        
        // Custom configuration format
        DocStringType("config", (String docString) -> {
            Properties props = new Properties();
            props.load(new StringReader(docString));
            return new Configuration(props);
        });
        
        // SQL doc string transformation
        DocStringType("sql", (String docString) -> {
            return new SqlQuery(docString.trim());
        });
    }
}

// Usage in step definitions
public class DocStringSteps implements En {
    public DocStringSteps() {
        When("I send the following JSON request:", (JsonNode requestJson) -> {
            apiClient.sendRequest(requestJson);
        });
        
        Then("the XML response should be:", (Document expectedXml) -> {
            Document actualXml = apiClient.getLastXmlResponse();
            assertXmlEquals(expectedXml, actualXml);
        });
        
        Given("the following CSV data:", (List<List<String>> csvData) -> {
            dataProcessor.loadCsvData(csvData);
        });
        
        When("I apply this configuration:", (Configuration config) -> {
            applicationContext.applyConfiguration(config);
        });
        
        Then("the database should contain:", (SqlQuery expectedQuery) -> {
            List<Map<String, Object>> results = database.query(expectedQuery.getSql());
            assertResultsMatch(expectedQuery, results);
        });
    }
}

Doc String Functional Interface

/**
 * Transform doc string content to custom object
 */
@FunctionalInterface
public interface DocStringDefinitionBody<T> {
    /**
     * Transform doc string to custom type
     * @param docString Multi-line string content from Gherkin doc string
     * @return Transformed object of type T
     * @throws Throwable Any exception during transformation
     */
    T accept(String docString) throws Throwable;
}

Default Transformers

Fallback transformers that handle conversions when no specific transformer is defined. Ideal for integration with object mappers like Jackson for automatic conversion.

/**
 * Default transformation fallbacks for parameters and data tables
 */
default void DefaultParameterTransformer(DefaultParameterTransformerBody body) { ... }
default void DefaultDataTableCellTransformer(DefaultDataTableCellTransformerBody body) { ... }
default void DefaultDataTableEntryTransformer(DefaultDataTableEntryTransformerBody body) { ... }

/**
 * Default transformers with empty cell replacement
 */
default void DefaultDataTableCellTransformer(String replaceWithEmptyString, DefaultDataTableCellTransformerBody body) { ... }
default void DefaultDataTableEntryTransformer(String replaceWithEmptyString, DefaultDataTableEntryTransformerBody body) { ... }

Usage Examples:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JavaType;

public class DefaultTransformerDefinitions implements En {
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    public DefaultTransformerDefinitions() {
        // Default parameter transformer using Jackson
        DefaultParameterTransformer((String fromValue, Type toValueType) -> {
            JavaType javaType = objectMapper.getTypeFactory().constructType(toValueType);
            return objectMapper.convertValue(fromValue, javaType);
        });
        
        // Default data table cell transformer
        DefaultDataTableCellTransformer((String fromValue, Type toValueType) -> {
            if (toValueType == Integer.class) {
                return Integer.parseInt(fromValue);
            }
            if (toValueType == Double.class) {
                return Double.parseDouble(fromValue);
            }
            if (toValueType == Boolean.class) {
                return Boolean.parseBoolean(fromValue);
            }
            if (toValueType == LocalDate.class) {
                return LocalDate.parse(fromValue);
            }
            return fromValue; // Fallback to string
        });
        
        // Default data table entry transformer using Jackson
        DefaultDataTableEntryTransformer((Map<String, String> fromValue, Type toValueType) -> {
            JavaType javaType = objectMapper.getTypeFactory().constructType(toValueType);
            return objectMapper.convertValue(fromValue, javaType);
        });
        
        // Default transformer with empty cell replacement
        DefaultDataTableEntryTransformer("[empty]", (Map<String, String> fromValue, Type toValueType) -> {
            // "[empty]" in cells becomes empty string before transformation
            JavaType javaType = objectMapper.getTypeFactory().constructType(toValueType);
            return objectMapper.convertValue(fromValue, javaType);
        });
    }
}

Default Transformer Functional Interfaces

/**
 * Default parameter transformer for unmatched parameter types
 */
@FunctionalInterface
public interface DefaultParameterTransformerBody {
    /**
     * Transform parameter value to target type when no specific transformer exists
     * @param fromValue String value to transform
     * @param toValueType Target Java type for transformation
     * @return Transformed object of target type
     * @throws Throwable Any exception during transformation
     */
    Object accept(String fromValue, Type toValueType) throws Throwable;
}

/**
 * Default data table cell transformer for unmatched cell types
 */
@FunctionalInterface
public interface DefaultDataTableCellTransformerBody {
    /**
     * Transform cell value to target type when no specific transformer exists
     * @param fromValue String cell value to transform
     * @param toValueType Target Java type for transformation
     * @return Transformed object of target type
     * @throws Throwable Any exception during transformation
     */
    Object accept(String fromValue, Type toValueType) throws Throwable;
}

/**
 * Default data table entry transformer for unmatched entry types
 */
@FunctionalInterface
public interface DefaultDataTableEntryTransformerBody {
    /**
     * Transform entry to target type when no specific transformer exists
     * @param fromValue Map representing table row to transform
     * @param toValueType Target Java type for transformation  
     * @return Transformed object of target type
     * @throws Throwable Any exception during transformation
     */
    Object accept(Map<String, String> fromValue, Type toValueType) throws Throwable;
}

Empty Cell Handling

Data tables in Gherkin cannot represent null or empty strings unambiguously. Cucumber interprets empty cells as null, but you can configure replacement strings for empty values.

// Configure replacement for empty cells
DataTableType("[blank]", (Map<String, String> entry) -> new User(
    entry.get("name"),
    entry.get("email")  // "[blank]" becomes empty string, null stays null
));

// Gherkin table:
// | name  | email        |
// | Alice | alice@ex.com |  
// | Bob   | [blank]      |  <- becomes empty string
// | Carol |              |  <- becomes null

Install with Tessl CLI

npx tessl i tessl/maven-io-cucumber--cucumber-java8

docs

hooks.md

index.md

step-definitions.md

test-context.md

transformers.md

tile.json