CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-cucumber--cucumber-java

Cucumber-JVM Java library providing annotation-based step definitions for Behavior-Driven Development (BDD) testing

Pending
Overview
Eval results
Files

parameter-transformation.mddocs/

Parameter Transformation

Custom parameter types for converting step parameters from strings to domain objects, with extensive configuration options. Cucumber-Java provides a sophisticated transformation system including custom parameter types, default transformers, and data table transformations.

Capabilities

Custom Parameter Types

Register custom parameter types to convert strings from step definitions into domain objects using regular expressions.

/**
 * Register custom parameter types for Cucumber expressions
 * @param value Regular expression pattern for matching
 * @param name Parameter type name (default: method name)  
 * @param preferForRegexMatch Preferential type for regex matching (default: false)
 * @param useForSnippets Use in snippet generation (default: false)
 * @param useRegexpMatchAsStrongTypeHint Provide strong type hint (default: false)
 */
@ParameterType(value = "regex", 
               name = "typeName",
               preferForRegexMatch = false,
               useForSnippets = false, 
               useRegexpMatchAsStrongTypeHint = false)
public CustomType parameterTransformer(String input) {
    return new CustomType(input);
}

/**
 * Multi-capture group parameter type
 */
@ParameterType("(\\d+)-(\\d+)-(\\d+)")
public LocalDate date(String year, String month, String day) {
    return LocalDate.of(Integer.parseInt(year), 
                       Integer.parseInt(month), 
                       Integer.parseInt(day));
}

Usage Examples:

import io.cucumber.java.ParameterType;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class ParameterTypes {
    
    // Simple parameter type
    @ParameterType("\\d{4}-\\d{2}-\\d{2}")
    public LocalDate date(String dateString) {
        return LocalDate.parse(dateString);
    }
    
    // Custom name parameter type
    @ParameterType(value = "\\d+", name = "number")
    public BigInteger bigNumber(String numberString) {
        return new BigInteger(numberString);
    }
    
    // Multi-capture group parameter type
    @ParameterType("(\\d{1,2})/(\\d{1,2})/(\\d{4})")
    public LocalDate usDate(String month, String day, String year) {
        return LocalDate.of(Integer.parseInt(year),
                           Integer.parseInt(month), 
                           Integer.parseInt(day));
    }
    
    // Enum parameter type  
    @ParameterType("red|green|blue")
    public Color color(String colorName) {
        return Color.valueOf(colorName.toUpperCase());
    }
    
    // Complex object parameter type
    @ParameterType("([A-Za-z]+)\\s+([A-Za-z]+)\\s+<([^>]+)>")
    public Person person(String firstName, String lastName, String email) {
        return new Person(firstName, lastName, email);
    }
    
    // Parameter type for snippets
    @ParameterType(value = "\\d+\\.\\d{2}", useForSnippets = true)
    public Money money(String amount) {
        return new Money(new BigDecimal(amount));
    }
    
    // Preferential parameter type
    @ParameterType(value = "\\d+", preferForRegexMatch = true)
    public CustomNumber customNumber(String number) {
        return new CustomNumber(Integer.parseInt(number));
    }
}

// Usage in step definitions
@Given("the date is {date}")
public void the_date_is(LocalDate date) {
    // date is automatically converted from string
}

@When("I transfer {money} to {person}")
public void transfer_money(Money amount, Person recipient) {
    // Both parameters automatically converted
}

Default Parameter Transformer

Register default parameter transformer for handling all unmatched parameter conversions.

/**
 * Register default parameter transformer
 * Method signatures supported:
 * - String, Type -> Object (transform from string)
 * - Object, Type -> Object (transform from any object)
 */
@DefaultParameterTransformer
public Object defaultTransformer(String fromValue, Type toValueType) {
    // Default transformation logic
    return transformedValue;
}

/**
 * Alternative signature for object transformation
 */
@DefaultParameterTransformer  
public Object defaultTransformer(Object fromValue, Type toValueType) {
    // Transform from any object type
    return transformedValue;
}

Usage Examples:

import io.cucumber.java.DefaultParameterTransformer;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;

public class DefaultTransformers {
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    // JSON-based default transformer using Jackson
    @DefaultParameterTransformer
    public Object defaultTransformer(String fromValue, Type toValueType) {
        try {
            // Try JSON transformation first
            return objectMapper.readValue(fromValue, 
                                        objectMapper.constructType(toValueType));
        } catch (Exception e) {
            // Fallback to string-based transformation
            if (toValueType.equals(Integer.class)) {
                return Integer.valueOf(fromValue);
            }
            if (toValueType.equals(Boolean.class)) {
                return Boolean.valueOf(fromValue);
            }
            if (toValueType.equals(LocalDate.class)) {
                return LocalDate.parse(fromValue);
            }
            return fromValue; // Return as string if no transformation
        }
    }
    
    // Enum-aware default transformer
    @DefaultParameterTransformer
    public Object enumTransformer(String fromValue, Type toValueType) {
        if (toValueType instanceof Class && ((Class<?>) toValueType).isEnum()) {
            Class<? extends Enum> enumClass = (Class<? extends Enum>) toValueType;
            return Enum.valueOf(enumClass, fromValue.toUpperCase());
        }
        return fromValue;
    }
}

// Usage - these will use default transformer
@Given("the status is {Status}")  // Uses enum transformer
public void the_status_is(Status status) { }

@When("the config is {Config}")   // Uses JSON transformer  
public void the_config_is(Config config) { }

Data Table Type Transformers

Register transformers for converting data table entries into custom objects.

/**
 * Register data table type transformer
 * @param replaceWithEmptyString Values to replace with empty string (default: {})
 */
@DataTableType(replaceWithEmptyString = {"[blank]", "[empty]"})
public CustomObject tableEntryTransformer(Map<String, String> entry) {
    return new CustomObject(entry);
}

/**
 * Data table type for list transformation
 */
@DataTableType
public CustomObject listTransformer(List<String> row) {
    return new CustomObject(row.get(0), row.get(1));
}

/**
 * Data table type for single cell transformation
 */
@DataTableType
public CustomObject cellTransformer(String cell) {
    return new CustomObject(cell);
}

Usage Examples:

import io.cucumber.java.DataTableType;
import java.util.Map;
import java.util.List;

public class DataTableTypes {
    
    // Transform table rows to Person objects
    @DataTableType
    public Person personEntry(Map<String, String> entry) {
        return new Person(
            entry.get("name"),
            entry.get("email"), 
            Integer.parseInt(entry.get("age"))
        );
    }
    
    // Transform with empty string replacement
    @DataTableType(replaceWithEmptyString = {"", "[empty]", "null"})
    public Product productEntry(Map<String, String> entry) {
        return new Product(
            entry.get("name"),
            entry.get("description").isEmpty() ? null : entry.get("description"),
            new BigDecimal(entry.get("price"))
        );
    }
    
    // Transform list rows to objects
    @DataTableType
    public Coordinate coordinateRow(List<String> row) {
        return new Coordinate(
            Double.parseDouble(row.get(0)), // x
            Double.parseDouble(row.get(1))  // y
        );
    }
    
    // Single cell transformation
    @DataTableType
    public Currency currencyCell(String cell) {
        return Currency.getInstance(cell);
    }
}

// Usage in step definitions
@Given("the following users exist:")
public void users_exist(List<Person> users) {
    // Each table row automatically converted to Person
}

@When("I create products:")
public void create_products(List<Product> products) {
    // Each table row automatically converted to Product
}

Default Data Table Transformers

Register default transformers for data table entries and cells.

/**
 * Register default data table entry transformer
 * @param headersToProperties Convert headers to properties format (default: true)
 * @param replaceWithEmptyString Values to replace with empty string (default: {})
 */
@DefaultDataTableEntryTransformer(headersToProperties = true,
                                  replaceWithEmptyString = {})
public Object defaultEntryTransformer(Map<String, String> entry, Type type) {
    return transformedEntry;
}

/**
 * Register default data table cell transformer  
 * @param replaceWithEmptyString Values to replace with empty string (default: {})
 */
@DefaultDataTableCellTransformer(replaceWithEmptyString = {})
public Object defaultCellTransformer(String cell, Type type) {
    return transformedCell;
}

Usage Examples:

import io.cucumber.java.DefaultDataTableEntryTransformer;
import io.cucumber.java.DefaultDataTableCellTransformer;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;

public class DefaultDataTableTransformers {
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    // JSON-based default entry transformer
    @DefaultDataTableEntryTransformer
    public Object defaultEntryTransformer(Map<String, String> entry, Type type) {
        try {
            // Convert map to JSON then to target type
            String json = objectMapper.writeValueAsString(entry);
            return objectMapper.readValue(json, 
                                        objectMapper.constructType(type));
        } catch (Exception e) {
            throw new RuntimeException("Failed to transform entry: " + entry, e);
        }
    }
    
    // Property-style header conversion
    @DefaultDataTableEntryTransformer(headersToProperties = true)
    public Object propertyEntryTransformer(Map<String, String> entry, Type type) {
        // Headers like "First Name" become "firstName" properties
        Map<String, String> propertyMap = entry.entrySet().stream()
            .collect(Collectors.toMap(
                e -> toCamelCase(e.getKey()),
                Map.Entry::getValue
            ));
        
        return createObjectFromProperties(propertyMap, type);
    }
    
    // Default cell transformer with multiple replacements
    @DefaultDataTableCellTransformer(replaceWithEmptyString = {
        "[blank]", "[empty]", "null", "N/A", ""
    })
    public Object defaultCellTransformer(String cell, Type type) {
        // Handle common type conversions
        if (type.equals(Integer.class) && !cell.isEmpty()) {
            return Integer.valueOf(cell);
        }
        if (type.equals(Boolean.class)) {
            return Boolean.valueOf(cell);
        }
        if (type.equals(LocalDate.class) && !cell.isEmpty()) {
            return LocalDate.parse(cell);
        }
        return cell;
    }
    
    private String toCamelCase(String header) {
        return Arrays.stream(header.split("\\s+"))
            .map(word -> word.toLowerCase())
            .collect(Collectors.joining())
            .replaceFirst("^.", header.substring(0, 1).toLowerCase());
    }
}

Doc String Type Transformers

Register transformers for converting doc strings into custom objects.

/**
 * Register doc string type transformer
 * @param contentType Content type for matching (default: "" - uses method name)
 */
@DocStringType(contentType = "json")
public CustomObject docStringTransformer(String docString) {
    return parseDocString(docString);
}

/**
 * Doc string type using method name as content type
 */
@DocStringType
public XmlDocument xml(String docString) {
    return XmlDocument.parse(docString);
}

Usage Examples:

import io.cucumber.java.DocStringType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DocStringTypes {
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    // JSON doc string transformer
    @DocStringType
    public JsonNode json(String docString) {
        try {
            return objectMapper.readTree(docString);
        } catch (Exception e) {
            throw new RuntimeException("Invalid JSON: " + docString, e);
        }
    }
    
    // XML doc string transformer  
    @DocStringType
    public Document xml(String docString) {
        try {
            DocumentBuilder builder = DocumentBuilderFactory
                .newInstance()
                .newDocumentBuilder();
            return builder.parse(new InputSource(new StringReader(docString)));
        } catch (Exception e) {
            throw new RuntimeException("Invalid XML: " + docString, e);
        }
    }
    
    // Custom content type
    @DocStringType(contentType = "yaml")
    public Map<String, Object> yamlContent(String docString) {
        return yamlParser.parse(docString);
    }
    
    // Configuration object from properties
    @DocStringType(contentType = "properties")
    public Properties propertiesContent(String docString) {
        Properties props = new Properties();
        try {
            props.load(new StringReader(docString));
            return props;
        } catch (Exception e) {
            throw new RuntimeException("Invalid properties: " + docString, e);
        }
    }
}

// Usage in step definitions
@Given("the following JSON configuration:")
public void json_configuration(JsonNode config) {
    // DocString automatically parsed as JSON
}

@When("I send the XML request:")  
public void send_xml_request(Document xmlDoc) {
    // DocString automatically parsed as XML
}

@Then("the YAML response should be:")
public void yaml_response(Map<String, Object> yaml) {
    // DocString automatically parsed as YAML
}

Data Table Transposition

Use the @Transpose annotation to transpose data tables before transformation.

/**
 * Transpose annotation for data table parameters
 * @param value Whether to transpose the table (default: true)
 */
public void stepWithTransposedTable(@Transpose DataTable table) { }

/**
 * Transpose with explicit value
 */
public void stepWithTransposedTable(@Transpose(true) List<Map<String, String>> data) { }

/**
 * Conditional transposition
 */
public void stepWithConditionalTranspose(@Transpose(false) DataTable table) { }

Usage Examples:

import io.cucumber.java.Transpose;
import io.cucumber.datatable.DataTable;

public class TransposeExamples {
    
    // Transpose data table for vertical layout
    @Given("the user has the following profile:")
    public void user_profile(@Transpose DataTable profileData) {
        // Original table:    | name  | John    |
        //                   | email | john@...| 
        //                   | age   | 30      |
        //
        // After transpose:  | name | email    | age |
        //                   | John | john@... | 30  |
    }
    
    // Transpose list of maps
    @Given("the settings are configured:")
    public void settings_configured(@Transpose List<Map<String, String>> settings) {
        // Transposed before converting to list of maps
        for (Map<String, String> setting : settings) {
            String key = setting.get("key");
            String value = setting.get("value");
        }
    }
    
    // Conditional transpose
    @Given("the matrix data:")
    public void matrix_data(@Transpose(false) DataTable matrix) {
        // Table is NOT transposed
    }
    
    // Transpose with custom objects
    @Given("the product attributes:")
    public void product_attributes(@Transpose List<Attribute> attributes) {
        // Table transposed then converted to Attribute objects
    }
}

Install with Tessl CLI

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

docs

data-handling.md

hooks.md

index.md

parameter-transformation.md

runtime-context.md

step-definitions.md

tile.json