Add cucumber tags and feature file programmatically

1k views Asked by At

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 ?

1

There are 1 answers

0
Krishnan Mahadevan On BEST ANSWER

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.