3

What I want

I am working with DataTables in Cucumber.

I have the following situation:

Step in the feature file:

I set the ingredients necessary for a meal
  | name           | ingredients        |
  | mac and cheese | pasta,cheese       |
  | hamburger      | bread,meat,lettuce | 

In the StepDefinition file I have

@When("I set the ingredients necessary for a meal")
public void setIngredients(List<Meal> meals){
  //do things with it
}

And I have a class Meal

public class Meal {
  String name;
  List<String> ingredients;
}

This doesn't work.

what I know

If I set my ingredients field as a simple String Cucumber "magically" matches both name and ingredients to the fields of the class and, in the step definition I will get a List of Meals correctly filled. But as it currently is it doesn't automatically match.

What I tried

I tried defining the class as:

public class Meal {
  String name;
  String ingredients;
  List<String> ingredientsList;
}
  • And having a constructor Meal(String, String) that would parse the ingredients into the ingredients list, but it doesn't work.

  • I tried defining a setter for ingredients that would parse it and also define the ingredientsList but it also doesn't work.

  • I tried using a DataTable in the step definition but still I can't find a way to transform it into my list of ingredients.

  • I tried using Transformer but, AFAIK, I would have to define a step for each meal I want to serve and I would have to sent the values within the step itself.

What I don't want

I don't want to be forced to parse the information anywhere but the Meal class.

How I temporarily solved it

Within the more complete Meal definition, a defined a setIngredientsList() that parses the ingredients into a list.

On the step definition, I iterate through the list of meals and call setIngredientsList for each of them. As I said, I don't want any of this processing done outside of the Meal class.

Question

Does anyone know how I can do this please?

Inês
  • 882
  • 3
  • 13
  • 28

2 Answers2

1

I would consider using a Map<String, String> instead of a list. Looking at your examples looks like name value pairs to me. A map might be a good fit.

When your fields are package private in Meal you probably don't need a constructor. I would, however, hide them as private variables and add a two parameter constructor. The habit of exposing as little as possible tend to support long term maintenace.

I wrote a blog post a while back that might help you.

Thomas Sundberg
  • 4,098
  • 3
  • 18
  • 25
0

You can use the @XStreamConverter annotation to register your XStream converter class.

The column names in the datatable and fields in the desired POJO need to be the same for Cucumber to assign them values.

Create a converter by extending AbstractSingleValueConverter for the parsing logic.

public class IngredientConverter extends AbstractSingleValueConverter {

    @Override
    public boolean canConvert(Class type) {
        return type.equals(ArrayList.class);
    }

    @Override
    public Object fromString(String ingreds) {
        return Arrays.asList(ingreds.split(","));
    }
}

Annotate the ingredients field with @XStreamConverter(IngredientConverter.class)

public class Meal {

    private String name;

    @XStreamConverter(IngredientConverter.class)
    private List<String> ingredients;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getIngredients() {
        return ingredients;
    }

    public void setIngredients(List<String> ingredients) {
        this.ingredients = ingredients;
    }    
}

If you are using Cucumber2 then look at these two links which allow global registration of custom xstreamconverters. These are now part of official cucumber release. Link 1 Link 2

Grasshopper
  • 8,908
  • 2
  • 17
  • 32