distributing cucmber junit5 tests in docker - cucumber

I have cucumber project that is running on azure build and release pipelines.
I have installed docker and docker-compose and I am able to to open containers and grid using docker-selenium through docker-compose
I would like to distribute cucumber tests , somewhat like as explained here . But in that link , it is explained using testng and I am using junit5 with cucumber . How would I achieve that , specially the below part from the link , in conjunction with what we know about cucumber parallelism in junit5?
#Parameters({"Port"})
#BeforeClass
public void initiateDriver(String Port) throws MalformedURLException {
if(Port.equalsIgnoreCase("9001"))
{
driver = new RemoteWebDriver(new URL("http:localhost:4444/wd/hub"), DesiredCapabilities.chrome());
driver.manage().window().maximize();
}
else if(Port.equalsIgnoreCase("9002")){
driver = new RemoteWebDriver(new URL("http:localhost:4444/wd/hub"), DesiredCapabilities.firefox());
driver.manage().window().maximize();
}
}
As I understand , if I mention cucumber.execution.parallel.enabled=true , in junit-platform.properties , it will run parallely per feature file , how do I mention port to achieve below
Run each feature in one container
Is there a way to distribute each scenario of different feature files in separate container ?

Typically you'd let your CI run a matrix of tests. This matrix of tests can already run different jobs in parallel.
Each job in the matrix can execute either all tests or a slice of tests. You use tags to include or exclude certain tests on certain job configurations. This ensures that browser and OS specific problems are immediately apparent.
This also simplifies the problem somewhat. When you don't start a Selenium Grid with different browser types, you don't have to find a clever way to connect each test to the right browser.
Instead you each job only has to start a Selenium Grid with one browser type. Once the grid has been started each job can run tests in parallel by using multiple remote webdrivers.
Then all you have to take care of is to make sure your tests don't try to use more browsers then the grid has available. This can be done by setting some configuration parameters:
cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=fixed
cucumber.execution.parallel.config.fixed.parallelism=4
You have to get these configuration parameters to your test execution. One way to do this would be to use a maven profile, but there are many other ways to do this:
<profile>
<id>parallelism</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<properties>
<configurationParameters>
cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=fixed
cucumber.execution.parallel.config.fixed.parallelism={env.CONCURRENT_TESTS}
</configurationParameters>
</properties>
</configuration>
</plugin>
</plugins>
</build>
</profile>
And you could tie this together like this:
test:
stage: test
script:
- ./start-selenium-grid $BROWSER $CONCURRENT_TESTS
- mvn test -Pparallelism
parallel:
matrix:
- OS: Windows
OS_VERSION: 10
BROWSER: [Chrome, Firefox, Edge]
CONCURRENT_TESTS: 4
- OS: OS X
OS_VERSION: Big Sur
BROWSER: [Chrome, Firefox, Edge, Safari]
CONCURRENT_TESTS: 2
So in summary. First run build jobs in parallel for different browsers. Then inside each job, run tests in parallel against multiple web drivers.

I could not find any solution for parallelizing junit5 tests at scenario level so I used testNG with courgette-jvm in conjunction with cucumber-guice. It worked out of the box and run parallel test at scenario level
Simply inlclude similar runner class in cucumber. My tests are further using RemoteWebdriver to open multiple instances on selenium grid. Make sure grid is up and running and node is registered to the grid.
import courgette.api.CourgetteOptions;
import courgette.api.CourgetteRunLevel;
import courgette.api.CucumberOptions;
import courgette.api.testng.TestNGCourgette;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
#Test
#CourgetteOptions(
threads = 10,
runLevel = CourgetteRunLevel.SCENARIO,
rerunFailedScenarios = true,
rerunAttempts = 1,
showTestOutput = true,
reportTitle = "Courgette-JVM Example",
reportTargetDir = "build",
environmentInfo = "browser=chrome; git_branch=master",
cucumberOptions = #CucumberOptions(
features = "src/test/resources/com/test/",
glue = "com.test.stepdefs",
publish = true,
plugin = {
"pretty",
"json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html"}
))
class AcceptanceIT extends TestNGCourgette {
}
RemoteWebdriver config is
protected RemoteWebDriver createDriver() throws MalformedURLException , IOException {
Properties properties = new Properties();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String hubURL = "http://192.168.1.7:65299/wd/hub";
System.setProperty("webdriver.gecko.driver", "/Users/amit/Desktop/amit/projects/misc/geckodriver");
FirefoxProfile profile = new FirefoxProfile();
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability(FirefoxDriver.PROFILE, profile);
capabilities.setPlatform(Platform.ANY);
FirefoxOptions options = new FirefoxOptions();
options.merge(capabilities);
driver.set(new RemoteWebDriver(new URL(hubURL),options));
return driver.get();
}
cucumber-guice related configs
import io.cucumber.guice.ScenarioScoped;
#ScenarioScoped
public class Global {
public RemoteWebDriver driver;
public Global() throws MalformedURLException, IOException {
driver = new DriverFactory().getManager();
// getManager() should have driver from above createDriver()
}
}
Inject Global class in your stepdefs like below (do necessary imports). You can inject helper classes also and guice will have it available per scenario
#Inject
Global global;
//then you can do
global.driver.get(site);
pom for cucumber-guice
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-guice</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
Use below config , if you want to open the grid using docker-compose and point your tests to http://localhost:65299/wd/hub . Ports are according to the below file
version: "3"
services:
selenium-hub:
image: selenium/hub:3.141.59-20210607
container_name: selenium-hub
ports:
- "65299:4444"
chrome:
image: selenium/node-chrome:3.141.59-20210607
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444
firefox:
image: selenium/node-firefox:3.141.59-20210607
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444
deploy:
mode: replicated
replicas: 7

Related

Cucumber rerun failed tests and cocatenate results using a testNG runner for cucumber 4.x

I am trying to execute a 2nd round of running only the failed scenarios in order to avoid false alarms because of environment/network/test unstabilities.
I am wondering if there is way to run only a maven command that will perform the following:
1. run all the scenarios that my runner includes in its configuration
2. create the cucumber.json report for this run
3. create a list of the failed scenarios
4. run the list of the failed scenarios
5. change the result of the report created in 1rst run depending on the results of failed tests in 2nd run, so that at the end, only the scenarios that failed in both runs are marked as failed and also there are no duplicates in the final report for those tests that failed at 1rst run (i.e. 2nd run overides the result of 1st run).
I am using cucumber 4.4.0 and testNG runner.
I have tried to use #ExtendedCucumberOptions (http://mkolisnyk.github.io/cucumber-reports/extended-cucumber-runner) and the 1rst run was ok, but the 2nd run was never happened, throwing the following exception:
"Method public void com.github.mkolisnyk.cucumber.runner.ExtendedTestNGRunner.feature(cucumber.api.testng.CucumberFeatureWrapper) requires a #DataProvider named : feature"
I found this issue:
https://github.com/mkolisnyk/cucumber-reports/issues/138
So, it seems that this cucumber-reports library does not support later versions of cucumber and I cannot use #ExtendedCucumberOptions that seemed to provide exactly what I need.
===> My maven dependecies include:
<dependency>
<groupId>com.github.mkolisnyk</groupId>
<artifactId>cucumber-runner</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>4.4.0</version>
</dependency>
===> And my runner class :
package runners;
import cucumber.api.CucumberOptions;
import cucumber.api.testng.AbstractTestNGCucumberTests;
import org.testng.annotations.DataProvider;
#CucumberOptions(
features = {"src/test/resources/features/"},
glue = {"stepDefinitions"},
tags = {"#magic"},
plugin = {
"pretty"
, "html:target/cucumber"
, "json:target/cucumber/cucumber.json"
, "rerun:target/cucumber/failedTests.txt"
},
monochrome = false
)
public class AreaRunner extends AbstractTestNGCucumberTests {
#Override
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
Is there any solution other than #ExtendedCucumberOptions?
The reason the 2nd run never happened is because Retry functionality is applicable for JUnit runner only, as per https://github.com/mkolisnyk/cucumber-reports/issues/167.
Try https://github.com/prashant-ramcharan/courgette-jvm - it has the ability to rerun failing tests similarly to ExtendedCucumberOptions, but this one contains the new Cucumber already.

I keep getting the error, "Unimplemented substep definition" in IntelliJ with Cucumber?

I have already:
Downloaded the Cucumber Java, Gherkin plugin
I already have the steps and features directories:
My directory structure looks like this:
- test
- java
- features
- featureSet1
- oneFeature.feature
- anotherFeature.feature
- featuresSet2
- twoFeature.feature
- CucumberTests.java
- steps
- step1.java
- step2.java
Under the features folder, I have a file called, CucumberTests.java. I'm able to run the tests via mvn test but the red error marks reallllly annoy me.
I have these tags in CucumberTest.java, which is supposed to run the tests:
#RunWith(Cucumber.class)
#CucumberOptions(plugin = { "pretty", "html:target/surefire-
reports/cucumber", "json:target/surefire-
reports/cucumberOriginal.json"},
features = {"src/test/java/features/featuresSet1",
"src/test/java/features/featuresSet2",
},
tags = {"~#ignore"},
glue = {"steps"})
The issue is from Substeps IntelliJ Plugin that IntelliJ suggests you install when it locates a .feature file inside your project.
Just ignore this extension when it pops up or uninstall if you already have it.
Cucumber for Java and Gherkin should be enough.

groovy.sql.Sql Springboot cli

I'm attempting to create a small Springboot application that uses the groovy.sql.Sql class to connect to an Oracle database(the Oracle jar has already been grabbed and is in the spring boot classpath). This is a very simple example for proof of concept/testing.
import groovy.sql.Sql
#RestController
class ThisWillActuallyRun {
#RequestMapping("/")
String home() {
oracleSql = Sql.newInstance(jdbc:oracle:thin:#oracle-db:1521:db-name,
"oracle-user",
"oracle-pass",
"oracle.jdbc.driver.OracleDriver")
row = oracleSql.firstRow("select foo from blah")
return "ok"
}
}
When the application is ran using the command:
spring run test_for_so.groovy
The following error is produced:
startup failed:
file:test_for_so.groovy: 1: unable to resolve class groovy.sql.Sql # line 1, column 1.
import groovy.sql.Sql
What groovy jars are you including as dependencies? The groovy jar only includes the base language support. Change it to groovy-all to get the full language and library package. For example, in gradle, use:
dependencies {
compile "org.codehaus.groovy:groovy-all"
...
}
In maven, it would look like this:
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</dependency>
</dependencies>
You can also just add the groovy-sql on top of the groovy package if you don't want everything.
I had same problem. I just found the solution.
Add below code.
#Grab('groovy-sql')
After groovy-sql you can use
import groovy.sql.Sql

Integrating Node.js with QUnit to Jenkins

This is my first sof post so forgive my format and organization of thought. I've made a great effort to solve my problem before posting this. Part of my issue could be lack of knowledge with packages in Ubuntu or Node.js so please guide me.
I'm trying to create a XUnit xml file for Jenkins from QUnit tests for a Node.js application. I don't have the ability to run a browser or even a headless browser, also don't understand why I'd need one since the Node.js code doesn't deal with the browser.
I've been searching all over and have only been successful using qunit-tap and 'prove' to create an XML file. Prove required downloading a formatter which was a perl file. We are trying to prevent using perl stuff.
My system is an Ubuntu VM. This is a task for work and my boss is asking for the minimal amount of packages and dependencies. Our Node.js server is accepting web socket requests and passing messages back and forth with a legacy system written in php.
QUnit's output seems to be a pretty print format, in a table, when I run my tests in the console. It would be amazing to just get that into a flatter form with a flag!
Thanks in advance!
Well, for NodeJS you can go with Grunt and grunt-contrib-qunit, though I would recommend the following approach:
Leverage JUnit Logger
(https://github.com/jquery/qunit-reporter-junit) plugin for JUnit
compatible report.
Comment out console.log output in the PhantomJS Runner https://github.com/jquery/qunit/tree/master/addons/phantomjs to mute non-XML output produced by the runner
Assign a task for Apache Ant build script:
<target name="qunit" description="runs QUnit tests using PhantomJS">
<echo message="Executing QUnit Javascript Unit Tests..."/>
<exec executable="/usr/local/bin/phantomjs" output="./build/qunit/qunit-results.xml">
<arg value="./vendors/Runner/runner-muted.js" />
<arg value="test-runner.html" />
</exec>
</target>
Jenkins shall look for the report in ./build/qunit/qunit-results.xml
ditto, above answer good
use the junit report out and connect it to the standard jenkins unit testing plugin -> http://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin
for nice easy to configure self-installed nodejs on the machine, i have to recommend the excellent -> http://wiki.jenkins-ci.org/display/JENKINS/NodeJS+Plugin
for jshint/csslint reports, i found the https://wiki.jenkins-ci.org/display/JENKINS/Checkstyle+Plugin plugin very nice, jshint and csslint both output to this
jshint : {
options : {
reporter : 'checkstyle',
reporterOutput : 'reports/jshint.xml',
},
src : "..."
},
csslint : {
strict : {
options : {
formatters : [{
id : 'checkstyle-xml',
dest : 'reports/csslint.xml'
}],
csslintrc: '.csslintrc'
},
src : [...],
},
},

Logback configuration in Groovy and different class paths in Gradle/Idea

I am using SLF4J and Logback for logging in a Groovy application, therefore I also configure logback via Groovy configuration. My config is very easy and looks like:
import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.DEBUG
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.status.OnConsoleStatusListener
// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)
// setting up appenders
appender('CONSOLE', ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%d [%thread] %-5level %logger - %msg%n"
}
}
// setting up loggers
logger("org.apache", INFO)
root(DEBUG, ['CONSOLE'])
However, I came into the problem that org.apache logging messages also occur on debug level which shouldn't be according to the config above (I also tried to change the order of looger and root). I found out that the config isn't loaded at all when running the app from IDEA. But when I start it via command line (just executing gradlew run) everything works.
Question 1: Does anyone know what happens here? It seems that the class path started from IDEA is different than the class path started from gradlew and that there is another logback config file available. But the classpath in IDEA is only java and the gradle dependencies. In my opionion (just read it from here) logback should look for a logback.groovy at first and I am quite sure that my file is the only one.
Then I tried to translage a working XML config to Groovy via the online translator. The config is just the same but I noticed that the logback debug option gets lost:
<configuration debug="true">
...
</configuration>
Question 2: Does anyone know how to enable logback debug via Groovy? (debug = true is NOT working)
The full Gradle build file:
// Plugins
apply plugin: 'groovy'
apply plugin: 'application'
apply plugin: 'idea'
// Dependencies
configure(allprojects)
{
ext.groovy = '2.1.0'
ext.slf4jVersion = '1.7.2'
ext.logbackVersion = '1.0.9'
ext.apacheFluentHc = '4.2.3'
}
repositories {
mavenCentral()
}
configurations {
compile.exclude module: 'commons-logging'
}
dependencies {
// Groovy
compile "org.codehaus.groovy:groovy-all:${groovy}:indy"
// Logging
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-classic:$logbackVersion"
compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
// Apache HttpClient
compile "org.apache.httpcomponents:fluent-hc:$apacheFluentHc"
}
// Java options
sourceCompatibility = 1.7
targetCompatibility = 1.7
mainClassName = 'XXX'
// Groovy options
[compileGroovy.groovyOptions, compileTestGroovy.groovyOptions]*.with {
fork = true
optimizationOptions = [ indy: true, 'int': false]
encoding = 'UTF-8'
}
// Tasks
task wrap(type:Wrapper, description:"create a gradlew") {
gradleVersion = '1.4'
}
I solved question 1, logback.groovy just wasn't found and therefore a default config was loaded.
When I executed the application in IDEA, I did just run the mainclass and not gradle run. I am used to this from Eclipse, where the output path from Maven and Eclipse are ident. But this is not the case with IDEA and Gradle. While the output path for IDEA is out\production\<project>, it is build\classes\main for Gradle and IDEA does not copy the resources to it's output path.
Now I use gradle run within IDEA and therefore bypass the IDEA build.
Question 2 is just not implemented (Logback 1.0.11), there is no DSL for debug, see code.

Resources