Silviu
Silviu

Reputation: 11

How to set dynamically CucumberOptions in Java

I'm developing a desktop app that will execute automated test cases. From the GUI the user is able to select the execution type: by feature/by scenario/by tagName. Now I'm using maven and execute commands in CMD to execute test cases and i would like to switch to TestNG execution but I need to set dynamically from a configuration file the CucumberOptions,

@CucumberOptions(features = { "src/test/resources/features/" }, tags = { "@Test3" }, glue = { "stepdefinitions" }, plugin = { "listeners.ExtentCucumberFormatter:" })
public class TestNGRunner extends AbstractTestNGCucumberTests {
}

but CucumberOptions require a constant expression and I cannot set them.

Is there any way how I can do it?

Upvotes: 1

Views: 6223

Answers (2)

Tihamer
Tihamer

Reputation: 971

Note: This is from Java, which I run from Eclipse, not the command line. Not that there is a huge difference if you know what you're doing, but still...

I had an Excel spreadsheet where each row contained a roles and a subset of screens, and I already had finished all my Gherkin feature files with @tag scenarios (and their corresponding Java/Selenium code snippets) that tested one screen. Some roles only had 4 screens; some had up to 46. I did not want to create 24 (and potentially more) different Cucumber Runners (one for each row in the Excel spreadsheet). I first tried changing the tags dynamically on an existing Cucumber Runner by editing the tags property of the CucumberOptions annotation (in Java; it's possible!), but I couldn't get it to accept String arrays of multiple tags; only individual tags. :-( Then I stumbled on how cucumber.api.cli.Main worked, and that made everything easier. The most important classes are cucumber.runtime.Runtime and cucumber.runtime.RuntimeOptions. I'm going to skip all the POI code for reading the Excel file, but the important part is as follows:

import org.junit.Test;
import org.junit.internal.TextListener;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;

import cucumber.api.CucumberOptions;
import cucumber.api.SnippetType;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderClassFinder;

public class MyRunner {

    /** Constructor */
    public MyRunner (String thisPath) {
           // This does all the POI stuff, plus making the data available to
           //   getCellData(sheet, columnName, row) (which you get to define). 
           // Any way that makes it return a dynamic value will work here.
    }   
 
    public static void main(String[] args) {
        MyRunner myRunner = new MyRunner ("src/main/resources/User Roles.xlsx");
        myRunner.runRunner();
    }

    public void runRunner() {
        String[] newTags = { "@Login" }; 
        String sheetName = "Sheet1";
        int rowCount = this.getRowCount(sheetName);
        String role = "";
        String userId = "";
        String password = "";
        //For each row in the Excel file (row 1 is column names)
        for (int i=2; i<rowCount; i++) { 
            System.out.println("\n run loop i: " + i);
            int rowNumber = i-1;
            role = this.getCellData(sheetName, "User Role", rowNumber).trim();
            userId = this.getCellData(sheetName, "UserID", rowNumber).trim();
            password = this.getCellData(sheetName, "Password", rowNumber).trim();
            String screens = this.getCellData(sheetName, "Screens", rowNumber).trim(); 
            System.out.println(rowNumber + ". runRbac role: '" + role + 
                    "', userId: '" + userId + "', password: '" + password + 
                    "', screens: '" + screens + "'.");
            // I had to do some indirection here because customer who edits the
            // Excel file does not want to see Tags, but uses Screen titles.
            // So I had to map it.
            ArrayList<String> tagList = this.screenTitlesToTags(screens);
            System.out.println(rowNumber + ". Check " + tagList.size() + " screens.");
            runOneRow(rowNumber, role, userId, password, tagList);                          
        }
        String[] finalTags = { "@LogoutCloseBrowser" }; 
        runOneRow(rowCount+1, role, userId, password, finalTags);
    }


    public static void runOneRow(int iteration, String role, String userId, 
                      String password, ArrayList<String> tags) {
        System.out.println("Starting runOneRow with iteration=" + iteration + 
           ", userid=" + userId + ", " + password + ", role=" + role);
        // I couldn't figure out a way to inject the UserId and password into JUnit,
        // so I did it old-school: as a file that the snippet reads. Yuck!
        Utils.writeFile("target/credential.txt", userId + ", " + password); 
        // !!!!!! Here is the heart of the matter!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        JUnitCore junit = new JUnitCore();
        junit.addListener(new TextListener(System.out));
        System.out.println("runOneRow- JUnit/Cucumber test with tags: " + tags);
        String tagString = tags.toString().replace("[", "").replace("]", "").trim();
        tagString = StringUtils.removeEnd(tagString, ",");
        // The tagString value should look something like: 
        // "@Login, @WelcomePage, @FirstScreen, @SecondScreen, @ThirdScreen, @Screen4"
        String[] argv = { "--plugin",  "com.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/MyCukeReport.html",
                "--snippets",  "camelcase",
                "--glue", "com.myorg.cukes.stepDefinitions", // or wherever 
                "--tags",  tagString,
                "src/main/java/com/hhs/cms/cukes/features" };
        RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        ResourceLoader resourceLoader = new MultiLoader(classLoader);
        ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
        Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
        try {
            runtime.run();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Finished runOneRow with exitStatus=" + runtime.exitStatus() + ", iteration=" + iteration + ", userid=" + userId + ", " + password + ", role=" + role);
        Result jUnitResult = junit.run(cukeRunnerClass);
        resultReport(jUnitResult);
    }

}

Upvotes: 1

George
George

Reputation: 3030

You can pass Cucumber Options through command line arguments:

You can list the options available for the Cucumber version you are using.

Pass the --help option to print out all the available configuration options:

java cucumber.api.cli.Main --help

Or:

mvn test -Dcucumber.options="--help"

You can also use tags to specify what to run.

Configuration options can also be overridden and passed to any of the runners via the cucumber.options Java system property.

For example, if you are using Maven and want to run a subset of scenarios tagged with @smoke:

mvn test -Dcucumber.options="--tags @smoke"

provide additional mechanisms for passing options to Cucumber.Some of the runners

Cucumber Options Documentation

Upvotes: 4

Related Questions