Neha
Neha

Reputation: 316

Conversion of Cucumber DataTable to Map

I am using Cucumber Data Tables. I want to convert this Data Table into a Map rather than a list. So basically what if I use the Header Row as Key and the data rows as value for the key. How should I do that?

Let me share 1 example to be more clear.

Given the following animals:

  | Type  | BabyAnimal |    
  | cow   | Calf       |
  | horse | Pony       |
  | sheep | Lamb       |

Instead of creating a List<List<String>> here, it is a better approach to use a List<Map<String,String>> here. Map's Key should contain 'Type' and 'BabyAnimal' and values should contain the respective values. So the Map entities would be:

<Type,cow>,<BabyAnimal,Calf>
<Type,horse>,<BabyAnimal,Pony>
<Type,sheep>,<BabyAnimal,Lamb>

How would we do that? I feel this is a better approach of doing because we are fetching the data from the keys. eg List(1).Map.get(Type) Whereas in case of List we would have to do a get(0), get(1) and there are chances of using incorrect data.

Upvotes: 7

Views: 31390

Answers (6)

Brad
Brad

Reputation: 15879

Perhaps the API has moved on since this question was first posted. I'm using cucumber version 4.8.1 and this code gives what you need without the cumbersome Lists of Maps

Map<String, String> map = dataTable.asMap(String.class, String.class);

Upvotes: 3

Tristan Zhou
Tristan Zhou

Reputation: 81

In Cucumber 3.0.2, just call asMaps() from DataTable.

public void feature_step(DataTable dataTable) {
    List<Map<String, String>> data =  dataTable.asMaps();
    String type = data.get(0).get("Type");
    Strig babyAnimal = data.get(0).get("BabyAnimal");
}

Upvotes: 2

M.P. Korstanje
M.P. Korstanje

Reputation: 12029

And adding a second answer because the generic types in your question got munched by the html.

Given the following animals:

| Type  | BabyAnimal |    
| cow   | Calf       |
| horse | Pony       |
| sheep | Lamb       |

And assuming you want this to be your step definition:

@Given("all baby animal details")
public void allMapDetails(List<Map<String, String>> animals) {
    System.out.println(animals);
}

Then the table will be automatically converted to a list of maps of string to string.

Upvotes: 5

M.P. Korstanje
M.P. Korstanje

Reputation: 12029

Given the following animals:

| Type  | BabyAnimal |    
| cow   | Calf       |
| horse | Pony       |
| sheep | Lamb       |

And assuming you want this to be your step definition:

@Given("all baby animal details")
public void allMapDetails(Map<Type,BabyAnimal> animals) {
    System.out.println(animals);
}

When you define these paramter types:

public class ParameterTypes implements TypeRegistryConfigurer {

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

    @Override
    public void configureTypeRegistry(TypeRegistry typeRegistry) {
        typeRegistry.defineDataTableType(new DataTableType(
            Type.class,
            (Map<String, String> row) -> new Type(row.get("Type"))
        ));

        typeRegistry.defineDataTableType(new DataTableType(
            BabyAnimal.class,
            (Map<String, String> row) -> new BabyAnimal(row.get("BabyAnimal"))
        ));
    }
}

Then the table will be converted to a map of animal type to baby animal.

Upvotes: 1

Grasshopper
Grasshopper

Reputation: 9058

As mentioned in Marit's answer, if you are on v3 plus you will need to work a bit more. Instead of a getting back a List of Maps, you can get back a custom object which contains the List of Maps.

Refer to this to figure out about a configuration class to mention all the datatable conversions - Cucumber-JVM - io.cucumber.datatable.UndefinedDataTableTypeException

Place this bit of code into this class.

registry.defineDataTableType(new DataTableType(Animals.class, new TableTransformer<Animals>() {
            @Override
            public Animals transform(DataTable table) throws Throwable {
                Animals animals = new Animals();
                table.asMaps().forEach(e -> animals.addAnimal(e));
                return animals;
            }
        }));

This is the container or dataobject class.

public class Animals {

    public static final String type = "type";   
    public static final String baby = "babyanimal";

    private List<Map<String, String>> details = new ArrayList<>();

    public void addAnimal(Map<String, String> entry) {
        details.add(entry);
    }

    public List<Map<String, String>> getDetails() {
        return details;
    }

    @Override
    public String toString() {
        return "Animals [details=" + details + "]";
    }
}

Step definition

@Given("all baby animal details")
public void allMapDetails(Animals anim) {
    System.out.println(anim.getDetails());
}

You should get something like this -

[{type=horse, babyanimal=Pony}, {type=sheep, babyanimal=Lamb}, {type=cow, babyanimal=Calf}]

Upvotes: 2

Marit
Marit

Reputation: 3026

You can find the following DataTable hint in the cucumber-jvm project on GitHub:

        "    // For automatic transformation, change DataTable to one of\n" +
        "    // E, List<E>, List<List<E>>, List<Map<K,V>>, Map<K,V> or\n" +
        "    // Map<K, List<V>>. E,K,V must be a String, Integer, Float,\n" +
        "    // Double, Byte, Short, Long, BigInteger or BigDecimal.\n" +
        "    //\n" +
        "    // For other transformations you can register a DataTableType.\n";

You can find how, in the Cucumber documentation:

The simplest way to pass a List to a step definition is to use a data table:

Given the following animals:
  | cow   |
  | horse |
  | sheep |

Declare the argument as a List, but don’t define any capture groups in the expression:

@Given("the following animals:")
public void the_following_animals(List<String> animals) {
}

In your case, replace List with Map.

Upvotes: 1

Related Questions