简体   繁体   中英

TestNG & Cucumber: Pass in single feature file name as parameter

I'd like to have a single testNG/Cucumber runner & single testng @Test that allows me to pass in the name of a single feature file as a testng parameter (using @Parameter) and run it. I'd like a runtime solution.

I have a bunch of non-Cucumber tests using already written using the TestNG framework and I'd like to have the Cucumber code in there as well.

Has anyone come up with something clever ?

Figured out how you can send custom cucumberoptions which contains the feature file name to the runner class. This will allow both cucumber and non-cucumber tests to be run from testng.xml .

Below text is based on details in "Cucumber for Java" book...


Cucumber checks if any option overrides have been provided for @CucumberOptions annotation. Following are checked from top to bottom, stops after any one is found:

  1. The OS environment variable CUCUMBER_OPTIONS
  2. The Java system property cucumber.options
  3. The Java resource bundle cucumber.properties with a cucumber.options property

Values found in override will replace any values set except for the plugin argument. Plugin argument will be appended. Arguments which are not overridden will not be affected.


testng.xml

<suite name="Default suite">    
    <test name="Cucumber Mix">
        <classes>
            <class name="cucumtestng.test.RunAbstractSampleTest"></class>
            <class name="cucumtestng.test.NormalTest"></class>
        </classes>
    </test>
</suite>


@CucumberOptions(features="",glue="cucumtestng.test.stepdefs",snippets=SnippetType.CAMELCASE,
plugin={"pretty", "html:report", "json:reports.json"})
public class RunAbstractSampleTest extends AbstractTestNGCucumberTests {

}


public class NormalTest {
  @Test
  public void f() {
      System.out.println("NORMAL TESTNG CLASS");
  }
}

You can also use testng cucumber classes which do not extend AbstractTestNgCucumberTests but use composition...

Setup Run As Configuration in eclipse as below and run... 在此处输入图片说明 在此处输入图片说明

This "setup" code does the trick. It gives me the cucumber feature I'm interested in running. I will look at the other proposal as well.

@Parameters({"featurename"})
@BeforeTest(alwaysRun = true)
public void setUpTest(String featureName) throws Exception {
    testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
    List<CucumberFeature> featureList = testNGCucumberRunner.getFeatures();

    for (CucumberFeature feature : featureList){
        if (featureName.equalsIgnoreCase(feature.getPath())){
            runFeature = feature;
            break;
        }
    }
}

Another technique is using reflection to modify the CucumberOptions annotation in real time before instantiating the runner (thanks to several older posts for the inspiration):

@Parameters({"featurePath"})
@BeforeTest(alwaysRun = true)
public void setUpTest(@Optional("src/main/java/cucumberFeatureFiles/Testcase.feature") String featurePath) throws Exception {   
    Class<?> testClass = this.getClass();
    changeCucumberAnnotation(testClass, "features", new String [] {featurePath});       
    testNGCucumberRunner = new WaltersTestngCucumberRunner(testClass);        
}

private static void changeCucumberAnnotation(Class<?> clazz, String key, Object newValue) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{  
    Annotation options = clazz.getAnnotation(CucumberOptions.class);                   //get the CucumberOptions annotation  
    InvocationHandler proxyHandler = Proxy.getInvocationHandler(options);              //setup handler so we can update Annotation using reflection. Basically creates a proxy for the Cucumber Options class
    Field f = proxyHandler.getClass().getDeclaredField("memberValues");                //the annotaton key/values are stored in the memberValues field
    f.setAccessible(true);                                                             //suppress any access issues when looking at f
    Map<String, Object> memberValues = (Map<String, Object>) f.get(proxyHandler);      //get the key-value map for the proxy
    memberValues.remove(key);                                                          //renove the key entry...don't worry, we'll add it back
    memberValues.put(key,newValue);                                                    //add the new key-value pair. The annotation is now updated.
}//end method

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM