Is there an option to pass the features through dataprovider to execute cucumber features in parallel instead of scenarios?
import org.testng.annotations.DataProvider;
import io.cucumber.testng.AbstractTestNGCucumberTests;
public class RunCucumberTest extends AbstractTestNGCucumberTests{
#Override
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
Related
I upgrade spring boot version 2.2.0.RELEASE to 2.7.4. After that import cucumber.api.junit.Cucumber; is not supported. This is the code,
import cucumber.api.junit.Cucumber;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ReportBuilder;
import net.masterthought.cucumber.json.support.Status;
import net.masterthought.cucumber.presentation.PresentationMode;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
/**
* Class to generate html report from cucumber runner's json report output.
*/
public class CucumberReportRunner extends Cucumber {
private static final String PROJECT_NAME = "Hello Cucumber & Spring Boot";
private static final String BUILD_NUMBER = "1.0.0";
private static final String BRANCH_NAME = "master";
public CucumberReportRunner(Class clazz) throws InitializationError {
super(clazz);
}
#Override
public void run(RunNotifier notifier) {
super.run(notifier);
generateReport();
}
public static void generateReport() {
File reportOutputDirectory = new File("target/html");
List<String> jsonFiles = new ArrayList<>();
jsonFiles.add("target/cucumber-report.json");
Configuration configuration = new Configuration(reportOutputDirectory, PROJECT_NAME);
configuration.addPresentationModes(PresentationMode.RUN_WITH_JENKINS);
configuration.setNotFailingStatuses(Collections.singleton(Status.SKIPPED));
configuration.setBuildNumber(BUILD_NUMBER);
configuration.addClassifications("Build Number", configuration.getBuildNumber());
configuration.addClassifications("Branch Name", BRANCH_NAME);
ReportBuilder reportBuilder = new ReportBuilder(jsonFiles, configuration);
reportBuilder.generateReports();
}
}
In the code ,
extends Cucumber
import cucumber.api.junit.Cucumber;
#Override
public void run(RunNotifier notifier) {
super.run(notifier);
code lines are shown in error.
and terminal getting this error,
/workspase/test-project/src/test/java/com/textile/bdd/CucumberReportRunner.java:3: error: package cucumber.api.junit does not exist
import cucumber.api.junit.Cucumber;
^
/workspase/test-project/src/test/java/com/textile/bdd/CucumberReportRunner.java:18: error: cannot find symbol
public class CucumberReportRunner extends Cucumber {
^
symbol: class Cucumber
/workspase/test-project/src/test/java/com/textile/bdd/TestRunner.java:11: error: cannot find symbol
#RunWith(Cucumber.class)
^
symbol: class Cucumber
What would be the best possible options?
I am using cucumber BDD, testng, java to write some BDD test. I would like to mock static classes in order to write my test. However when I write this testrunner, it fails to initialize the BDD scenarios.
Complete Example(note the commented line PrepareForTest) :
import gherkin.events.PickleEvent;
import io.cucumber.testng.CucumberOptions;
import io.cucumber.testng.PickleEventWrapper;
import io.cucumber.testng.TestNGCucumberRunner;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
#CucumberOptions(
features = {
"src/test/resources/features/sample"
},
glue = {
"com.demo.stepdefinitions.sample"
},
plugin = {
"pretty",
"html:target/cucumber-reports/cucumber-pretty",
"json:target/cucumber-reports/sampple-report.json",
"rerun:target/cucumber-reports/sample-rerun.txt"
}
)
//#PrepareForTest({Util.class})
public class TestngWithDataproviderTest extends PowerMockTestCase {
private TestNGCucumberRunner testNGCucumberRunner;
private void mockActiveBucket() {
PowerMockito.mockStatic(Util.class);
PowerMockito.when(Util.getBucketId(any(Long.class))).thenReturn(3);
}
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
#Test(dataProvider = "users")
public void testMockStatic(String username){
System.out.println("username: " + username);
System.out.println("static test passed");
mockActiveBucket();
Assert.assertTrue(true);
}
#Test(groups = "cucumber scenarios", description = "Runs Cucumber Scenarios", dataProvider = "scenarios")
public void testCucumberCcenario(PickleEventWrapper pickleEvent) throws Throwable {
PickleEvent event = pickleEvent.getPickleEvent();
mockActiveBucket();
testNGCucumberRunner.runScenario(pickleEvent.getPickleEvent());
Assert.assertTrue(true);
}
#DataProvider(name = "scenarios")
public Object[][] scenarios() {
Object[][] scenarios = testNGCucumberRunner.provideScenarios();
return new Object[][]{{scenarios[0][0]}};
}
#DataProvider(name = "users")
public Object[][] users() {
return new Object[][]{{"user1"}, {"user2"}};
}
}
class Util {
public static int getBucketId(long eventTimestamp){
Long minsPast5MinBoundary = (eventTimestamp % TimeUnit.MINUTES.toMillis(5))/TimeUnit.MINUTES.toMillis(1);
return minsPast5MinBoundary.intValue();
}
}
The above test fails to load BDD scenarios dataProvider if I enable PrepareForTest annotation on the test. However, the other test which uses dataProvider works fine in both cases(enable or disable PrepareForTest)
ERROR:
Data provider mismatch
Method: testCucumberCcenario([Parameter{index=0, type=io.cucumber.testng.PickleEventWrapper, declaredAnnotations=[]}])
Arguments: [(io.cucumber.testng.PickleEventWrapperImpl) "Sunday isn't Friday"]
at org.testng.internal.reflect.DataProviderMethodMatcher.getConformingArguments(DataProviderMethodMatcher.java:45)
at org.testng.internal.Parameters.injectParameters(Parameters.java:796)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:983)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
As a side effect of this, I am unable to mock static methods of util class while writing the BDD. I am new to cucumber BDD. Any help/pointers is appreciated.
After getting some help to root cause from #help-cucumber-jvm slack channel, I was able to root cause it to testng+powermock with dataproviders using custom classes.
For example this test fails
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.ObjectFactory;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
#PrepareForTest(Util2.class)
public class TestngWithDataproviderTestngTest extends PowerMockTestCase {
#ObjectFactory
public org.testng.IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
private void mockActiveBucket() {
PowerMockito.mockStatic(Util.class);
PowerMockito.when(Util.getBucketId(any(Long.class))).thenReturn(3);
}
#Test(dataProvider = "users")
public void testMockStatic(MyTestCaseImpl myTestCase) {
System.out.println("myTestCase: " + myTestCase);
System.out.println("static test passed");
mockActiveBucket();
Assert.assertTrue(true);
}
#DataProvider(name = "users")
public Object[][] users() {
return new Object[][]{{new MyTestCaseImpl(5)}};
}
}
//interface MyTestCase {
//}
class MyTestCaseImpl { //implements MyTestCase{
int i;
public MyTestCaseImpl() {
}
public MyTestCaseImpl(int i) {
this.i = i;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
class Util2 {
public static int getBucketId(long eventTimestamp) {
Long minsPast5MinBoundary = (eventTimestamp % TimeUnit.MINUTES.toMillis(5)) / TimeUnit.MINUTES.toMillis(1);
return minsPast5MinBoundary.intValue();
}
}
Here as mentioned, seems to be a known issue with a workaround. Hope this helps.
I was trying to achieve, Cucumber feature level parallel execution using pico Container.
When I am using a shared Driver in a context Class as below, I get org.picocontainer.PicoCompositionException: Duplicate Keys not allowed. Duplicate
public class Context{
private ThreadLocal<WebDriver> drivers = new ThreadLocal<>();
public void setDriver(WebDriver wd) {
drivers.set(wd);
}
public WebDriver getDriver() {
return drivers.get();
}
//Runner Class
import java.net.MalformedURLException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import cucumber.api.CucumberOptions;
import cucumber.api.testng.CucumberFeatureWrapper;
import cucumber.api.testng.TestNGCucumberRunner;
import net.thumbtack.cucumber.picocontainer.example.step.SharedDriver;
import cucumber.api.testng.*;
#CucumberOptions (glue = {"net.thumbtack.cucumber.picocontainer.example.step"},
features = "src/main/resources/"
,tags = {"#Scenario2,#Scenario3"})
public class TestRunner {
public TestRunner() throws MalformedURLException {
super();
// TODO Auto-generated constructor stub
}
private TestNGCucumberRunner testNGCucumberRunner;
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
System.setProperty("ExecEnv","Docker");
}
// #Test(dataProvider = "features")
// public void feature(PickleEventWrapper eventwrapper,CucumberFeatureWrapper cucumberFeature) throws Throwable {
#Test(groups="cucumber", description="Runs CucumberFeature",dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature){
testNGCucumberRunner.runCucumber(cucumberFeature.getCucumberFeature());
// testNGCucumberRunner.runScenario(eventwrapper.getPickleEvent());
}
#DataProvider(parallel=true)
public Object[][] features() {
return testNGCucumberRunner.provideFeatures();
// return testNGCucumberRunner.provideScenarios();
}
#AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
I'm looking at running Cucumber tests with TestNG. However I am having a issue where all my Scenario are running as one TestNG #Test session. Is there a way to run each Scenario as a separate #Test sessions?
Here is my TestNG xml:
<suite name="cucumber Suites">
<test name="cucumber-testing">
<classes>
<class name="runners.Run2" />
</classes>
</test>
</suite>
This will call run the following Test class:
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import cucumber.api.CucumberOptions;
import cucumber.api.testng.AbstractTestNGCucumberTests;
import cucumber.api.testng.CucumberFeatureWrapper;
import cucumber.api.testng.TestNGCucumberRunner;
#CucumberOptions( features="cucumber/features/example.feature",
glue="steps",
format={"pretty"}
)
public class Run2 extends AbstractTestNGCucumberTests{
private TestNGCucumberRunner tcr;
#BeforeClass(alwaysRun=true)
public void beforeClass() throws Exception{
tcr = new TestNGCucumberRunner(this.getClass());
}
#Test(groups="cucumber", description="Runs CucumberFeature", dataProvider="features")
public void feature(CucumberFeatureWrapper cucumberFeature){
tcr.runCucumber(cucumberFeature.getCucumberFeature());
}
#DataProvider
public Object[][] features(){
return tcr.provideFeatures();
}
#AfterClass (alwaysRun=true)
public void afterClass(){
tcr.finish();
}
}
I am wondering if there is a way to get the #DataProviderto provide Scenario and the #Test to run Scenario instead of the features?
The reason for this is that I have other TestNG tests with Listeners and that I want to use the same Listeners, reporting for Cucumber tests.
Thanks
What you can try is providing a specific test name for each feature:
#CucumberOptions( features="cucumber/features/example.feature",
glue="steps",
format={"pretty"}
)
public class Run2 extends AbstractTestNGCucumberTests implements ITest {
private TestNGCucumberRunner tcr;
private String featureName;
#BeforeClass(alwaysRun = true)
public void beforeClass() throws Exception {
tcr = new TestNGCucumberRunner(this.getClass());
}
#BeforeMethod
public void beforeMethod(Object[] params) {
CucumberFeatureWrapper cucumberFeature = (CucumberFeatureWrapper) params[0];
featureName = cucumberFeature.getCucumberFeature().getGherkinFeature().getName();
}
#Test(groups = "cucumber", description = "Runs CucumberFeature", dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature) {
tcr.runCucumber(cucumberFeature.getCucumberFeature());
}
#Override
public String getTestName() {
return featureName;
}
#DataProvider
public Object[][] features() {
return tcr.provideFeatures();
}
#AfterClass(alwaysRun = true)
public void afterClass() {
tcr.finish();
}
}
Let me know if it is the way you'd like.
Did you already try:
#CucumberOptions( features="cucumber/features/example.feature",
glue="steps",
format={"pretty"}
)
public class Run2 extends AbstractTestNGCucumberTests{
private TestNGCucumberRunner tcr;
#BeforeClass(alwaysRun=true)
public void beforeClass() throws Exception{
tcr = new TestNGCucumberRunner(this.getClass());
}
#Test(groups="cucumber", description="Runs CucumberFeature")
public void scenario(){
for (CucumberFeatureWrapper cucumberFeature : tcr.provideFeatures()) {
tcr.runCucumber(cucumberFeature.getCucumberFeature());
}
}
#AfterClass (alwaysRun=true)
public void afterClass(){
tcr.finish();
}
}
The answer is "Yes", you can run each scenario in cucumber as a test using TestNG.
How? It's explained below:
First of all update your Cucumber Maven dependencies from info.cukes to io.cucumber dependencies
Then instead of using method "provideFeatures()" of "TestNGCucumberRunner" in 'your' following code:
#DataProvider
public Object[][] features(){
return tcr.provideFeatures();
}
Use "provideScenarios()" method in #DataProvider.
The following Java code in Cucumber Runner Class worked perfectly for me to run each scenario as TestNG test in feature files:
public class TestRunner {
private TestNGCucumberRunner testNGCucumberRunner;
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
#Test(groups = "cucumber scenarios", description = "Runs Cucumber
Scenarios", dataProvider = "scenarios")
public void scenario(PickleEventWrapper pickleEvent, CucumberFeatureWrapper
cucumberFeature) throws Throwable{
testNGCucumberRunner.runScenario(pickleEvent.getPickleEvent());
}
#DataProvider
public Object[][] scenarios() {
return testNGCucumberRunner.provideScenarios();
}
#AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
I would be happy to see your problem resolved.
Reference: https://github.com/cucumber/cucumber-jvm/blob/master/testng/README.md
I followed this approach: https://github.com/cucumber/cucumber-jvm/blob/master/examples/java-calculator-testng/src/test/java/cucumber/examples/java/calculator/RunCukesByCompositionTest.java
One of the interactions I want to test is that a class Foo is supposed to pass a Stream<Changes> to FooListener.someChangesHappened. Is there a Mockito idiom to verify that a stream contained the expected objects?
Assuming you are just verifying the argument to a mock implementation, and are not actually using it, here is a custom Hamcrest Matcher that will get the job done. It gets hairy when you need to read from the Stream more than once, because Streams are not built for that. You'll notice that this solution even needs to protect itself from JUnit calling matches more than once.
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class Foo {
#Mock
FooListener fooListener;
#Before
public void happen() {
fooListener.someChangesHappened(Stream.of(Changes.ONE, Changes.TWO, Changes.THREE));
}
#Test
public void contains() {
verify(fooListener).someChangesHappened(argThat(streamThat(hasItem(Changes.TWO))));
}
#Test
public void doesNotContain() {
verify(fooListener).someChangesHappened(argThat(streamThat(not(hasItem(Changes.FOUR)))));
}
private static <T> Matcher<Stream<T>> streamThat(Matcher<Iterable<? super T>> toMatch) {
return new IterableStream<>(toMatch);
}
private interface FooListener {
void someChangesHappened(Stream<Changes> stream);
}
private enum Changes {
ONE, TWO, THREE, FOUR
}
private static class IterableStream<T> extends TypeSafeMatcher<Stream<T>> {
Matcher<Iterable<? super T>> toMatch;
List<T> input = null;
public IterableStream(Matcher<Iterable<? super T>> toMatch) {
this.toMatch = toMatch;
}
#Override
protected synchronized boolean matchesSafely(Stream<T> item) {
// This is to protect against JUnit calling this more than once
input = input == null ? item.collect(Collectors.toList()) : input;
return toMatch.matches(input);
}
#Override
public void describeTo(Description description) {
description.appendText("stream that represents ");
toMatch.describeTo(description);
}
}
}