15.5. Using data-driven testing for individual steps

Sometimes you want to use data-driven testing at the step level, rather than at the test level. For example, you might want to navigate to a particular screen in the application, and then try many combinations of data, or loop over a sequence of steps with data from a CSV file. This avoids having to reopen the browser for each row of data.

You can do this by adding property values to your Step files. Consider the following steps file:

        public class SampleDataDrivenSteps extends ScenarioSteps {

            public SampleDataDrivenSteps(Pages pages) {
                super(pages);
            }

            private String name;
            private String age;
            private String address;

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

            public void setAge(String age) {
                this.age = age;
            }

            public void setAddress(String address) {
                this.address = address;
            }

            @StepGroup
            public void enter_new_user_details() {
                enter_name_and_age(name, age);
                enter_address(address);
            }

            @Step
            public void enter_address(String address) {
                ...
            }

            @Step
            public void enter_name_and_age(String name, String age) {
                ...
            }

                @Step
                public void navigate_to_user_accounts_page() {
                        ...
                }
        }

The enter_personal_details step group uses the step fields to run the enter_name_and_age and enter_address steps. We want to fetch this data from a CSV file, and loop through the enter_personal_details step for each row of data.

We do this using the withTestDataFrom() method of the StepData class:

        import net.thucydides.core.annotations.ManagedPages;
        import net.thucydides.core.annotations.Steps;
        import net.thucydides.core.pages.Pages;
        import net.thucydides.junit.annotations.Managed;
        import net.thucydides.junit.runners.ThucydidesRunner;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.openqa.selenium.WebDriver;

        import static net.thucydides.core.steps.StepData.withTestDataFrom;

        @RunWith(ThucydidesRunner.class)
        public class SamplePassingScenarioWithTestSpecificData {

            @Managed
            public WebDriver webdriver;

            @ManagedPages(defaultUrl = "http://www.google.com")
            public Pages pages;

            @Steps
            public SampleDataDrivenSteps steps;


            @Test
            public void happy_day_scenario() throws Throwable {
                        steps.navigate_to_user_accounts_page();
                withTestDataFrom("test-data/simple-data.csv").run(steps).enter_new_user_details();
            }
        }

This will call the data_driven_test_step() multiple times, each time injecting data from the test-data/simple-data.csv file into the step.

You also can use as many data files as you want, even in the same test. You can also use the same data file for more than one test step. Remember only the properties that match columns in the CSV file will be instantiated - the others will be ignored:

        @RunWith(ThucydidesRunner.class)
        public class SamplePassingScenarioWithTestSpecificData {

            @Managed
            public WebDriver webdriver;

            @ManagedPages(defaultUrl = "http://www.google.com")
            public Pages pages;

            @Steps
            public SampleDataDrivenSteps steps;

            @Steps
            public DifferentDataDrivenSteps different_steps;


            @Test
            public void happy_day_scenario() throws Throwable {
                        steps.navigate_to_user_accounts_page();

                withTestDataFrom("test-data/simple-data.csv").run(steps).enter_new_user_details();

                withTestDataFrom("test-data/some_other-data.csv").run(different_steps).enter_other_details();
            }
        }

Note that, as a shortcut, you can dispense with the setter methods and just declare the relevant fields public. So the Steps class shown above could be rewritten like this:

        public class SampleDataDrivenSteps extends ScenarioSteps {

            public SampleDataDrivenSteps(Pages pages) {
                super(pages);
            }

            public String name;
            public String age;
            public String address;

            @StepGroup
            public void enter_new_user_details() {
                enter_name_and_age(name, age);
                enter_address(address);
            }

            @Step
            public void enter_address(String address) {
                ...
            }

            @Step
            public void enter_name_and_age(String name, String age) {
                ...
            }

                @Step
                public void navigate_to_user_accounts_page() {
                        ...
                }
        }