Parallel execution in testNG using Maven - multithreading

My scenario is to launch multiple chrome browsers(minimum 2) in Parallel.
I have created a separate class for WebDriver initialization, also I have 2 xml files and in that file it has 2 tests each.
WebDriver Initialization
public class LaunchBrowser
{
public WebDriver driver;
public WebDriver initDriver() {
if (driver == null) {
System.setProperty("webdriver.chrome.driver", "C:\\selenium\\drivers\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
}
return driver;
}
}
XML file 1 : test method 1
public class Stackoverflow extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
initDriver();
driver.get("https://stackoverflow.com");
Thread.sleep(3000);
System.out.println("Stack");
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}
XML file 1 : Test method 2
public class StackLogin extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
driver.findElement(By.xpath("//a[#href='https://stackoverflow.com/users/login?
ssrc=head&returnurl=https%3a%2f%2fstackoverflow.com%2f']")).click();
Thread.sleep(3000);
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}
XML file 2 : Test method 1
public class Google extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
initDriver();
driver.get("https://www.google.co.in");
Thread.sleep(3000);
System.out.println("Google");
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}
XML file 2 : Test Method 2
public class Gmail extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
driver.findElement(By.xpath("//a[#href='https://mail.google.com/mail/?tab=wm'][text()='Gmail']")).click();
Thread.sleep(3000);
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}
testng1.xml
<suite name="Suite1">
<test name="01Stackoverflow">
<classes>
<class name="com.ci.selenium.Stackoverflow" />
</classes>
</test>
<test name="02StackLogin">
<classes>
<class name="com.ci.selenium.StackLogin" />
</classes>
</test>
</suite>
testng2.xml
<suite name="Suite2">
<test name="1Google">
<classes>
<class name="com.ci.selenium.Google"/>
</classes>
</test>
<test name="2Gmail">
<classes>
<class name="com.ci.selenium.Gmail"/>
</classes>
</test>
</suite>
Also I have made the below configurations in my pom.xml file
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<suiteXmlFiles>${file}</suiteXmlFiles>
<skipTests>false</skipTests>
<properties>
<property>
<name>suitethreadpoolsize</name>
<value>2</value>
</property>
</properties>
</configuration>
</plugin>
Finally I have triggered the XML file using the below maven command.
mvn clean test -Dfile=MyWork/testng1.xml,MyWork/testng2.xml
Result:
Two Chrome browsers were launched at a time, but only first test method in each xml file got passed and the second test in both xml files gets failed.
Kindly help me to fix this issue.
Logs
java.lang.NullPointerException
at com.ci.selenium.StackLogin.1test(StackLogin.java:12)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
... Removed 18 stack frames

I have launched multiple(minimum 2) chrome browsers in Parallel by using the below site.
Thread Local for Parallel Test Execution
To achieve this scenario, I have created 3 classes. One for WebDriver initialization and other classes for Threads.
Class 1
public class SetTestNG implements Runnable
{
public String xmlString;
public SetTestNG(String suitXMLUrl){
xmlString = suitXMLUrl;
}
#Override
public void run(){
List<String> testSuites = Lists.newArrayList();
testSuites.add(xmlString);
TestNG testng = new TestNG();
testng.setTestSuites(testSuites);
testng.run();
}
}
Class 2 : Main Class
public class MultiThread
{
private static String inputFiles;
private static String[] xmlFile;
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, InterruptedException
{
inputFiles = args[0];
xmlFile = inputFiles.split(",");
for(String file : xmlFile)
{
Thread object1 = new Thread(new SetTestNG(System.getProperty("user.dir")+"/"+file));
object1.start();
Thread.sleep(5000);
}
}
}
Note: The main use case of this code to launch Chrome browser for every single testNG Suite files.

You again need to initilize driver in your second Test method of both class
public class LaunchBrowser
{
public WebDriver driver =null;
public WebDriver initDriver() {
if (driver == null) {
System.setProperty("webdriver.chrome.driver", "C:\\selenium\\drivers\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
}
return driver;
}
}
XML file 1 : Test method 2
public class StackLogin extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
initDriver();
driver.findElement(By.xpath("//a[#href='https://stackoverflow.com/users/login?
ssrc=head&returnurl=https%3a%2f%2fstackoverflow.com%2f']")).click();
Thread.sleep(3000);
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}
XML file 2 : Test Method 2
public class Gmail extends LaunchBrowser
{
#Test
public void 1test() throws InterruptedException
{
initDriver();
driver.findElement(By.xpath("//a[#href='https://mail.google.com/mail/?tab=wm'][text()='Gmail']")).click();
Thread.sleep(3000);
}
#Test
public void 2test() throws InterruptedException
{
Thread.sleep(3000);
}
}

It looks like you want your webdriver instance to be created only once per <suite> tag and then shared across #Test annotated test methods that reside amongst multiple <test> tags.
For achieving this, you would need to change your LaunchBrowser class to something like below :
public class LaunchBrowser {
protected WebDriver driver;
#org.testng.annotations.BeforeSuite
public void initDriver() {
System.setProperty("webdriver.chrome.driver", "C:\\selenium\\drivers\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
}
}
You also need to refactor all your tests, so that they don't explicitly call LaunchBrowser.initDriver() else, it would cause the webdriver instantiation to be happened twice - explicitly by your call and implicitly once by TestNG due to the method being annotated using a #BeforeSuite annotation.
That should solve your use case. But please remember that this is the most in-efficient way of managing your webdriver instance, because you are now strictly confined to sequential execution. You cannot run tests in parallel.

Related

Main application test always gives java.lang.IllegalStateException: Failed to load ApplicationContext

I am trying to run main application test class. But it's always gives Failed to Load Application context
Application.java
#SpringBootApplication
#ComponentScan(basePackages = { "com.aaa.abc","com.aaa.def" })
public class Application extends SpringBootServletInitilizer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure (SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
ApplicationTest.java
#SpringBootTest
class ApplicationTest {
#Test
void contextLoads() {
}
}
I tied by providing path of Application class in ApplicationTest class as specified below. But it's giving same exception
#SpringBootTest(classes={com.aaa.Application.class})

#SpringIntegrationTest annotation does not load context as expected

Normally, when I use #SpringBootTest I get the full context of beans. I can the #Autowire all kinds of beans that are available after the application has started.
Now, in the scope of spring-integration-test libary, the #SpringIntegrationTest does not do this.
As the testing module promises, you can use
#Autowired
private MockIntegrationContext mockIntegrationContext;
However, after inspecting the bean map on that instance, I found out there are no beans!
Example test:
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringIntegrationTest
public class AppTest {
#Autowired
private MockIntegrationContext mockIntegrationContext;
#Test
public void contextLoads() {
// put breakpoint to inspect field
System.out.println(mockIntegrationContext);
}
}
When I however run the following code, I get a complete context:
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest
public class App2Test {
#Autowired
private ListableBeanFactory beanFactory;
#Test
public void contextLoads() {
Assert.isTrue(beanFactory.getBeanDefinitionCount() > 0)
}
}
Why is that? How can I achieve a similar result with spring-integration-test?
Reading materials: https://docs.spring.io/spring-integration/docs/current/reference/html/testing.html
They are independent annotations; you need both.
EDIT
This works fine for me:
#RunWith(SpringRunner.class)
#SpringBootTest
#SpringIntegrationTest
public class So52297757ApplicationTests {
#Autowired
private MockIntegrationContext mockIntegrationContext;
#Autowired
private String foo;
#Test
public void contextLoads() {
System.out.println(foo);
System.out.println(mockIntegrationContext);
}
}
and
#SpringBootApplication
public class So52297757Application {
public static void main(String[] args) {
SpringApplication.run(So52297757Application.class, args);
}
#Bean
public String foo() {
return "foo";
}
}
and
foo
org.springframework.integration.test.context.MockIntegrationContext#1de5f0ef

How to run Cucumber scenario in different #Test or xml <test> with Cucumber & TestNG

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

How to create one instance of WebDriver per thread for parallel execution

I am taking input for test from Data Provider where Data Provider is used in parallel.
Method will run in parallel so i want to create separate instance of WebDriver per method
Tried till now:
public class Demo {
private static final ThreadLocal<WebDriver> webDriverThreadLocal= new InheritableThreadLocal<>();
public static Logger log = Logger.getLogger(Demo.class.getName());
#BeforeMethod
public void beforeMethod() {
WebDriver driver=null;
DOMConfigurator.configure("log4j.xml");
Random random = new Random();
int cnt=random.nextInt(2);
if(cnt == 0) {
driver = new FirefoxDriver();
System.out.println(" New Firefox Driver Instantiated");
log.info("New Firefox Driver Instantiated");
}
else if(cnt == 1) {
System.setProperty("webdriver.chrome.driver","path");
driver = new ChromeDriver();
System.out.println(" New Chrome Driver Instantiated");
log.info("New Chrome Driver Instantiated");
}
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
driver.manage().window().maximize();
webDriverThreadLocal.set(driver);
}
#Test(dataProvider = "dp1")
public void testPrelogin(TestCase testCase) {
WebDriver driver = webDriverThreadLocal.get();
//here call static methods of different classes for each screen
}
#DataProvider(name ="dp1",parallel=true)
public Object[][] dp() {
return new Object[][] {
new Object[] { 1, "a" },
new Object[] { 2, "b" },
};
}
#AfterMethod
public void afterClass() {
WebDriver driver = webDriverThreadLocal.get();
System.out.println("In after method for id:"+Thread.currentThread().getId()+" "+driver);
driver.quit();
}
Testng.xml
<suite name="Suite" parallel="methods" data-provider-thread-count="2">
<test name="prelogin" >
<classes>
<class name="com.package.Demo" />
</classes>
</test>
</suite>
With the above code multiple browsers are launched but out of 2 test atleast one of my test fails as browser screen becomes blank.
Is it because in some way thread resources are shared like webDriver or any other issue?

Facing issues while navigating between the reports in Serenity

I am able to run the serenity test cases by using gradle. I use the command $ gradle clean test aggregate. Reports are also getting generated however when I click on the links provided in the Reports it fails to navigate and gives an error message. I have created the package structure as mentioned in the link below.
http://thucydides.info/docs/articles/an-introduction-to-serenity-bdd-with-cucumber.html
However still I'm not able to resolve this. Below are my Runner, Step definition and repository class.
Runner Class:
#RunWith(CucumberWithSerenity.class)
#CucumberOptions(features = "src/test/resources/features/LoginFeatureSerenity.feature")
public class TestRunnerSerenity {
}
Step Definition class:
package org.gradle.stepdef;
public class LoginStepDefSerenity {
#Managed
public WebDriver driver;
#ManagedPages
public Pages pages;
LoginPageRepository page;
// Scenario 1: Verify New Serenity Test Case
#Step
#Given("^User is on LoginSerenity Page$")
public void user_is_on_LoginSerenity_Page() throws Throwable {
page.open();
}
#Step
#When("^User enters valid Serenity credentials$")
public void user_enters_valid_Serenity_credentials() throws Throwable {
page.setusername("kaustubhsaxena");
page.setpassword("saxenasdhfghjfg");
page.loginButton.click();
}
#Step
#Then("^User is able to login Serenity$")
public void user_is_able_to_login_Serenity() throws Throwable {
assertThat(page.loginValidationMessage.getText(), is("Login failed"));
// page.logoutButon.click();
driver.close();
}
}
Repository Class
#DefaultUrl("http://localhost:8000/app/#/login")
public class LoginPageRepository extends PageObject {
#FindBy(id = "username")
protected WebElement username;
public void setusername(String value) {
element(username).type(value);
}
public WebElementFacade username() {
return element(username);
}
// Fields for Password
#FindBy(id = "password")
protected WebElement password;
public void setpassword(String value) {
element(password).type(value);
}
public WebElementFacade password() {
return element(password);
}
}
Can you please help me on this. Thanks in advance
Fortunately I got the solution of this.
In the build.gradle below plugin needs to be added so that it will handle the reporting part.
apply plugin: 'com.jfrog.bintray'
Thanks for your help on this.

Resources