Parameter Converters

Converting step parameters

One of JBehave’s most useful features is the ability to automatically convert the parameter values in the candidate steps to objects and to look for steps with matching signatures. Start from the two-minute tutorial if you are not familiar with the basic JBehave functionality.

The ParameterConverters class is a facade responsible for converting parameter values to Java objects. At the heart of the conversion lies the ParameterConverter interface

public static interface ParameterConverter {
 
  boolean accept(Type type);
 
  Object convertValue(String value, Type type);
 
}

and several default built-in converters that JBehave supports:

  • NumberConverter
  • NumberListConverter
  • StringListConverter

The facade will iterate over all the parameter converters which have been defined and accept the first converter that matches the requested Type from the candidate step.

Type is the common superinterface for all types in Java since JDK 1.5 and is the generics-enabled equivalent to what was Class in pre-JDK 1.5 Java (Class implements Type so the uses of Class can be easily generalized to Type). ParameterizedType is a Type that allows for example to distinguish between a List<Number> and a List<String>, because while their raw type is the same (List), their actual type arguments are different (Number and String respectively).

Defining custom parameter converters

The power of parameter converters comes to the foreground when we need to define our own custom converters. Let’s look at an example from the source code, the trader example.

We have a scenario like:

Given a trader of name Mauro
Given a stock of prices 0.5,1.0
When the stock is traded at 2.0
Then the trader sells all stocks

As discussed above the number and number list parameters are automatically taken care of by the default built-in converters. But take at look at the first Given and the corresponding candidate step:

@Given("a trader of name $trader")
public void aTrader(Trader trader) {
    this.trader = trader;
}

We need a way to convert the parameter value which encodes the name of the trader to a Trader object, and this is where our custom parameter converter comes in play. The most obvious implementation might retrieve it from the persistence layer, eg.

public class TraderConverter implements ParameterConverter {
    private TraderPersister persister;
 
    public TraderConverter(TraderPersister persister) {
        this.persister = persister;
    }
 
    public boolean accept(Type type) {
        if (type instanceof Class) {
            return Trader.class.isAssignableFrom((Class<?>) type);
        }
        return false;
    }
 
    public Object convertValue(String value, Type type) {
        Trader trader = persister.retrieveTrader(value);
        if (trader == null) {
            throw new InvalidParameterException("Trader not found for name " + value, null);
        }
        return trader;
    }
}

but the actual implementation is completely customizable and in the users’ control.