Search code examples
javacucumbercucumber-java

Cucumber Java how to update an element in a DataTable


I am trying to follow an example in the Cucumber tutorial but it is written for Ruby and I am trying to write it in Java. I am having difficulty implementing the @When step as it requires me to update the DataTable and I am getting the following exception thrown,

java.lang.UnsupportedOperationException
at java.base/java.util.Collections$UnmodifiableList.set(Collections.java:1308)
at cucumber_tutorial.expressive_scenarios.chapter5.features.step_definitions.BoardSteps.player_x_plays_in_row_column(BoardSteps.java:36)
at ✽.When player x plays in row 2, column 1(tic_tac_toe.feature:8)

My Feature is as follows,

Feature:
  Scenario:
    Given a board like this:
      |   | 1 | 2 | 3 |
      | 1 |   |   |   |
      | 2 |   |   |   |
      | 3 |   |   |   |
    When player x plays in row 2, column 1
    Then the board should look like this:
      |   | 1 | 2 | 3 |
      | 1 |   |   |   |
      | 2 | x |   |   |
      | 3 |   |   |   |

After running the Feature and taking the generated Code snippets I have the following (I've added a couple of lines myself as well),

package cucumber_tutorial.expressive_scenarios.chapter5.features.step_definitions;

import cucumber.api.DataTable;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import java.util.List;

public class BoardSteps {

    List<List<String>> boardList;

    @Given("^a board like this:$")
    public void a_board_like_this(DataTable dataTable) throws Throwable {
        boardList = dataTable.raw();
        System.out.println(boardList);
        // Write code here that turns the phrase above into concrete actions
        // For automatic transformation, change DataTable to one of
        // List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>.
        // E,K,V must be a scalar (String, Integer, Date, enum etc)
        //throw new PendingException();
    }

    @When("^player x plays in row (\\d+), column (\\d+)$")
    public void player_x_plays_in_row_column(int row, int col) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        //board.
        //throw new PendingException()
        boardList.get(row).set(col, "x");
    }

    @Then("^the board should look like this:$")
    public void the_board_should_look_like_this(DataTable expectedTable) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        // For automatic transformation, change DataTable to one of
        // List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>.
        // E,K,V must be a scalar (String, Integer, Date, enum etc)
        expectedTable.diff(boardList);
        //throw new PendingException();
    }

}

The problem seems to be that dataTable.raw() in the @Given step assigns an Unmodifiable Collection type to boardList and so makes it un-updateable. The Ruby example simply has, ​ 

row, col = row.to_i, col.to_i​   
@board[row][col] = ​'x'​

my exception is thrown from,

boardList.get(row).set(col, "x"); //this line throws the exception

Can someone please advise me what the standard way is to update a DataTable using Java ?


Solution

  • As @Grasshopper suggested in the above comments, I have implemented a conversion function convertDataTableToModifiableList to convert the unmodifiable DataTable into a List<List<String>> object which I can update. My working solution is now as shown,

    import cucumber.api.DataTable;
    import cucumber.api.java.en.Given;
    import cucumber.api.java.en.Then;
    import cucumber.api.java.en.When;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class BoardSteps {
    
        List<List<String>> boardList;
    
        @Given("^a board like this:$")
        public void a_board_like_this(DataTable dataTable) throws Throwable {
            boardList = convertDataTableToModifiableList(dataTable);
            System.out.println(boardList);
            // Write code here that turns the phrase above into concrete actions
            // For automatic transformation, change DataTable to one of
            // List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>.
            // E,K,V must be a scalar (String, Integer, Date, enum etc)
            //throw new PendingException();
        }
    
        @When("^player x plays in row (\\d+), column (\\d+)$")
        public void player_x_plays_in_row_column(int row, int col) throws Throwable {
            // Write code here that turns the phrase above into concrete actions
            //board.
            //throw new PendingException()
            boardList.get(row).set(col, "x");
        }
    
        @Then("^the board should look like this:$")
        public void the_board_should_look_like_this(DataTable expectedTable) throws Throwable {
            // Write code here that turns the phrase above into concrete actions
            // For automatic transformation, change DataTable to one of
            // List<YourType>, List<List<E>>, List<Map<K,V>> or Map<K,V>.
            // E,K,V must be a scalar (String, Integer, Date, enum etc)
            expectedTable.diff(boardList);
            //throw new PendingException();
        }
    
        private List<List<String>> convertDataTableToModifiableList(DataTable dataTable){
            List<List<String>> lists = dataTable.asLists(String.class);
            List<List<String>> updateableLists = new ArrayList<>();
            for (int i = 0; i < lists.size(); i++){
                List<String> list = lists.get(i);
                List<String> updateableList = new ArrayList<>();
                for (int j = 0; j < list.size(); j++){
                    updateableList.add(j, list.get(j));
                }
                updateableLists.add(i, updateableList);
            }
            return updateableLists;
        }
    
    }
    

    I am surprised there isn't a more elegant way of doing this, if you have a better suggestion please let me know.