Search code examples
cucumbertestngcucumber-javacucumber-spring

Add cucumber tags and feature file programmatically


I am using spring-boot cucumber with TestNG to write and API test framework ,

wanted to undersatand how can add tags and feature file to executed based on environment selected

Below is my current implementation

@CucumberOptions(
    features = {"src/test/resources/Features"},
    glue = {"als.system.tests.stepDefinations"},
    plugin = {"pretty", "html:target/cucumber-html-report.html"}
)
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {

}

And skipping test based on tags , but this is not ideal solution and also dont want to display skipped test on report

@Before
public void setup(Scenario scenario) {
    if (!scenario.getSourceTagNames().contains("@" + productName.toLowerCase())) {
        throw new SkipException("Skipping /Ignoring this scenario as not part of executions !!!");
    }
}

Is there clean way to achieve this ?


Solution

  • Here's how you do it.

    • Ensure that you are using the latest released version of TestNG (it is 7.6.1 as of today and it needs JDK11)
    • Build a data provider interceptor by implementing the TestNG interface com.rationaleemotions.TagBasedInterceptor
    • Wire in this listener using either the <listener> tag (or) using the service provider interface approach. For details on this, you can refer to the official TestNG documentation here (or) refer to my blog-post here.

    Below is a sample implementation of the listener

    import io.cucumber.testng.PickleWrapper;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Optional;
    import org.testng.IDataProviderInterceptor;
    import org.testng.IDataProviderMethod;
    import org.testng.ITestContext;
    import org.testng.ITestNGMethod;
    
    public class TagBasedInterceptor implements IDataProviderInterceptor {
    
      @Override
      public Iterator<Object[]> intercept(Iterator<Object[]> original,
          IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) {
        String rawTag = iTestContext.getCurrentXmlTest().getParameter("tag");
        if (rawTag == null || rawTag.trim().isEmpty()) {
          return original;
        }
    
        List<String> tags = Arrays.asList(rawTag.trim().split(","));
        List<Object[]> pruned = new ArrayList<>();
        while (original.hasNext()) {
          Object[] currentElement = original.next();
          Optional<Object> searchResult = findPickleWrapper(currentElement);
          if (searchResult.isEmpty()) {
            continue;
          }
    
          PickleWrapper pickleWrapper = searchResult.map(element -> (PickleWrapper) element).get();
          boolean tagPresent = pickleWrapper.getPickle().getTags()
              .stream().anyMatch(tags::contains);
          if (tagPresent) {
            pruned.add(currentElement);
          }
        }
        return pruned.iterator();
      }
    
      private Optional<Object> findPickleWrapper(Object[] each) {
        return Arrays.stream(each)
            .filter(element -> element instanceof PickleWrapper)
            .findFirst();
      }
    }
    

    Here's how the suite xml would look like

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
    <suite name="Default Suite" verbose="2">
      <listeners>
        <listener class-name="com.rationaleemotions.TagBasedInterceptor"/>
      </listeners>
      <parameter name="tag" value="dragon_warrior"/>
      <test name="testng_playground">
        <classes>
          <class name="com.rationaleemotions.CucumberRunnerTests">
          </class>
        </classes>
      </test> 
    </suite> 
    

    Below are the dependencies that I am using for this sample

    <dependencies>
      <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
      <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-testng</artifactId>
        <version>7.8.0</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
      <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>7.8.0</version>
      </dependency>
    </dependencies>
    

    Feel free to enhance the listener such that if it also reads from the JVM argument (which you can specify using -D) so that you can have the dynamic behaviour of overriding the tag value in the suite xml with something that can be specified as a tag (or a comma separated list of tags) through the JVM argument.