Cucumber-JVM 4 Anonymous Parameter Types

Introduction

XStream was removed from Cucumber-JVM in version 3 for various reasons. Though it reduced a lot of complexities, it removed functionality which automatically transformed a String to a object and DataTable into a collection of objects. This now requires code for each transformation to define a DataTableType or ParamterType in the TypeRegistry for these cases. This has been made easier in Cucumber-JVM 4.2.0 by using Anonymous Parameter Types for Parameters and Jackson ObjectMapper for DataTables in 4.0.0. Refer to official announcements for version 4.0.0 and version 4.2.0.

This article explains in brief the migration of parameter from Cucumber-JVM version 2x to 4x. Refer these for a detailed explanation of migration from version 2 to version 3 of Parameter and Datatable.

Source Code & Versions

This article uses the Cucumber-JVM version 4.2.0. The source code for the article is located here. The source code for version 3.x and 2.x examples is located here.

Cucumber 2.x

This conversion from a String to a Java object having a single argument string constructor was handled automatically by XStream. The relevant parts of code for the feature file and step definition class are mentioned below.

Given the user name is John Doe

@Given("^the user name is (.*?)$")
public void theUserNameIs(User user) {
}

public class User {
    private String name; 
    public User(String name) {
        this.name = name;
    }
}

Cucumber 3.x to Cucumber 4.1.1

This conversion needs to be handled by defining a ParameterType in the TypeRegistry object. The User class remains the same. Cucumber Expression is now used in the step definition method. The relevant parts of code for the step definition class and typeregistry class are mentioned below. The feature file is the same so has been skipped.

@Given("the user name is {user}")
public void theUserNameIs(User user) {}

public class Configurer implements TypeRegistryConfigurer {
    @Override
    public void configureTypeRegistry(TypeRegistry registry) {
        registry.defineParameterType(new ParameterType<>("user", ".*?", 
        User.class, User::new));
    }
}

Cucumber 4.2.0 Anonymous Parameter Types

The automatic conversion is back without explicit code for each parameter type. This is accomplished by using an object mapping tool like Jackson. This also supports Enum and other types like BigInteger, BigDecimal, Byte, Short, Integer, Long, Float, Double, Date. Below changes need to be made from the previous version for this to work.

  1. Remove the explicit parameter type ie. defineParameterType method for the specific type from configureTypeRegistry method.
  2. Remove the parameter type variable within the curly brackets from the step definition pattern in case of Cucumber Expression. If this is not done then cucumber will search for the parameter type to be explicitly set as in the previous version.
  3. Create a transformer class that implements ParameterByTypeTransformer interface which does the actual type creation. This same class can also implement other interfaces like TableEntryByTypeTransformer, TableCellByTypeTransformer which make life easier when dealing with DataTable conversion introduced in version 4.0.0.
  4. Pass an object of the class created above to the setDefaultParameterTransformer method of the TypeRegistry object.

The relevant parts of code for the step definition class and typeregistry class are mentioned below. The feature file is the same so has been skipped.

@Given("the user name is {}")
public void theUserNameIs(User user) {
}

public class Configurer implements TypeRegistryConfigurer {
    @Override
    public void configureTypeRegistry(TypeRegistry registry) {
        JacksonTableTransformer jacksonTableTransformer = 
            new JacksonTableTransformer();
        registry.setDefaultParameterTransformer(jacksonTableTransformer);
    }

    @Override 
    public Locale locale() { 
        return Locale.ENGLISH; 
    }

    private static final class JacksonTableTransformer implements 
        ParameterByTypeTransformer {
        private final ObjectMapper objectMapper = new ObjectMapper();
        @Override
        public Object transform(String s, Type type) {
            return objectMapper.convertValue(s, objectMapper.constructType(type));
        }
    }
}

The case above is for the single arg constructor that takes a String parameter to be available or one has access to the object code to provide a suitable one. If this specific constructor is not present then this technique cannot be applied and the DataTableType needs to be registered in the TypeRegistry.

Leave a Reply

Your email address will not be published. Required fields are marked *