I am new to TestNG. I am able to do parallel testing with BeforeMethod and AfterMethod class where I am opening new driver and closing the same respectively.
Below is my code:
#BeforeMethod
public void run() {
driver.get().get(url);
driver.get().manage().window().maximize();
driver.get().manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
Demo_Page objDemoPage = new Demo_Page(driver.get(), prop, date);
objDemoPage.admin_demo("local");
Home_page objHomePage = new Home_page(driver.get(), prop, date);
objHomePage.pwd_change_later();
}
public WebDriver getDriver() {
return driver.get();
}
#AfterMethod
public void tearDown() {
getDriver().quit();
}
#Test(priority = 1)
public void Testcase1(String switch_browser, String domain_drop, String mode, String
domain, String first_name,
String last_name, String login_name, String emp_id, String pwd, String
conf_pwd, String simp_name,
String case_type, String manage_tab, String create_user, String account_tab,
String OU, String button_id,
String protect) {
WebDriver driver = getDriver();
XXX
XXX
XXX
}
Similarly, I have N number of #Test methods.
And my testNG will look like
<suite name="Suite" parallel="tests" thread-count="2">
<listeners>
<listener class-name="listeners3.Proj_Listener" />
</listeners>
followed by Test tags.
Here, each time, the driver is launched and corresponding tests are run and then the driver is closed. But, I want the thread-count times of browser to be launched for the very first time, and remaining test cases to be run on the same browsers(same thread whichever is available) instead of closing and launching again.
Could anyone please help?
Add this below method with the annotation to the test class.
#BeforeClass
public void before_class()
{
System.out.println("This method is executed before Class1 and class1 has your above test methods");
//Declare the driver at the class level : public WebDriver driver;
driver = new XXXDriver();
//now this object will be available to you across all the tests in the class
}
Related
I have two testcases that has to be executed
1.Login to an App
2.Perform some operations
Below is the design of my code:
BaseTest.java
public abstract class BaseTest {
public WebDriver driver;
#BeforeSuite
public void openApplication() {
System.setProperty(chrome_key,chrome_value);
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-notifications");
driver = new ChromeDriver(options);
driver.manage().window().maximize();
driver.get(url);
}
}
LoginPage.java
public class LoginPage extends BasePage{
#FindBy (xpath = "//input[#id='username']")
WebElement userName;
#FindBy (xpath = "//input[#id='password']")
WebElement password;
public LoginPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
public void loginToApp(String username, String pwd) {
}
}
Account.java:
public class NewAccount extends BasePage{
#FindBy(xpath = "//span[text()='Accounts']/../..")
WebElement accountsTab;
public NewAccount(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
public void createNewAccount() {
}
}
LoginToApp.java:
public class LoginToApp extends BaseTest {
#Test
public void a_verifyLogin() throws IOException {
LoginPage hp = new LoginPage(driver);
hp.loginToApp(username, password);
}
}
CreateAccount.java:
public class CreateAccount extends BaseTest {
#Test
public void b_createAccountRecord() {
NewAccount na = new NewAccount(driver);
na.createNewAccount();
}
}
testng.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Test">
<classes>
<class name="com.testcases.LoginToApp"/>
<class name="com.testcases.CreateAccount"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
With this framework structure, When I execute the testng.xml file, the first test in LoginToApp executes as expected. And When the control comes to the test in CreateAccount, the driver becomes Null and hence failing the execution of the 2nd Test.
Expected Flow:
1.Initialise Browser
2.Launch the url
3.Execute the #Test method of LoginToApp.java
4.Execute the #Test method of CreateAccount.java
Is it possible to achieve the above flow without making the WebDriver as static? If yes, please explain.
1.Initialise Browser (set this under before method)
2.Launch the url (give priority =0)
3.Execute the #Test method of LoginToApp.java (give priority =1)
4.Execute the #Test method of CreateAccount.java( give depends on method )
depends on method only execute if your loginapp test successfully run
I have created two page classes to locate the elements for the two web pages (named LoginPage.java and AddEmployee.java)in one package. After that in another package i have created two test classes for the above two corresponding classes respectively(named VerifyloginpageTest.java and VerifyAddEmployeeTest.java) also i have created another class named BrowserFactory.java to initialize browser and create driver instance. Using the page factory I want to first run login page Test cases in their priority order and then after successfully logging into the website it should move on to the add employee web page.
**
BrowserFactory.java
**
package OrangeTestCases.Helper;
public class BrowserFactory {
public static WebDriver driver;
#BeforeClass
public static WebDriver startBrowser(String browsername,String url)
{
System.setProperty("webdriver.chrome.driver","C:\\Users\\int120\\Downloads\\EXE FILES\\chromedriver_win32\\chromedriver.exe");
driver=new ChromeDriver();
driver.manage().window().maximize();
driver.get(url);
return driver;
}
**
LoginPage.java
package OrangeTestCases.orangeHRM.Pages;
public class LoginPage{
// WebDriver driver;
#FindBy(id="txtUsername")
WebElement username;
#FindBy(how=How.ID,using="txtPassword")
WebElement password;
#FindBy(how=How.ID,using="btnLogin")
WebElement loginBtn;
#FindBy(how=How.XPATH,using="//*[#id=\"spanMessage\"]")
WebElement errormsg;
#FindBy(how=How.XPATH,using="//*[#id=\"menu_dashboard_index\"]/b")
WebElement dashboard;
//public LoginPage(WebDriver driver) {
//
// this.driver=driver;
// }
public void typeUsername(String u_name)
{
username.clear();
username.sendKeys(u_name);
}
public void typePassword(String pass)
{
password.clear();
password.sendKeys(pass);
}
public void clickLogin()
{
loginBtn.click();
}
public String findErrorMsg()
{
String actual_error=errormsg.getText();
return actual_error;
}
public boolean findDashBorad()
{
boolean dashboard_display=dashboard.isDisplayed();
return dashboard_display;
}
}
**
AddEmployee.java
package OrangeTestCases.orangeHRM.Pages;
public class AddEmployee {
#FindBy(how=How.XPATH,using="//*[#id=\"menu_pim_viewPimModule\"]/b")
WebElement pim;
#FindBy(how=How.XPATH,using="//*[#id=\"menu_pim_addEmployee\"]")
WebElement addemployee;
#FindBy(how=How.XPATH,using="//*[#id=\"firstName\"]")
WebElement f_name;
#FindBy(how=How.XPATH,using="//*[#id=\"middleName\"]")
WebElement m_name;
#FindBy(how=How.XPATH,using="//*[#id=\"lastName\"]")
WebElement l_name;
#FindBy(how=How.XPATH,using="//*[#id=\"photofile\"]")
WebElement photo;
#FindBy(how=How.XPATH,using="//*[#id=\"chkLogin\"]")
WebElement create_chkbox;
#FindBy(how=How.ID,using="user_name")
WebElement username;
#FindBy(how=How.XPATH,using="//*[#id=\"user_password\"]")
WebElement pswrd1;
#FindBy(how=How.ID,using="re_password")
WebElement confirm_emp_pswrd;
#FindBy(how=How.XPATH,using="//*[#id=\"status\"]")
WebElement status;
#FindBy(how=How.XPATH,using="//*[#id=\"btnSave\"]")
WebElement save;
// WebDriver driver;
//
//
// public AddEmployee(WebDriver driver)
// {
// this.driver=driver;
// }
public void clickPIM()
{
pim.click();
}
public void clickAddEmployee()
{
addemployee.click();
}
public void typefirstname(String fname)
{
f_name.sendKeys(fname);
}
public void typemiddlename(String mname)
{
m_name.sendKeys(mname);
}
public void typelastname(String lname)
{
l_name.sendKeys(lname);
}
public void uploadPhoto(String photo1)
{
photo.sendKeys(photo1);
}
public void clickCheckbox()
{
create_chkbox.click();
}
public void typeUsername(String u_name)
{
username.sendKeys(u_name);
}
public void typePassword(String pass)
{
pswrd1.sendKeys(pass);
}
public void confirmPassword(String con_pass)
{
confirm_emp_pswrd.sendKeys(con_pass);
}
public void selectStatus(String status_test)
{
Select status1=new Select(status);
status1.selectByVisibleText(status_test);
}
public void clickSavebtn()
{
save.click();
}
}
**
VerifyloginpageTest.java
package OrangeTestCases.orangeHRM.Testcases;
import OrangeTestCases.Helper.BrowserFactory;
import OrangeTestCases.orangeHRM.Pages.LoginPage;
public class VerifyloginpageTest{
String url="https://opensource-demo.orangehrmlive.com/";
WebDriver driver=BrowserFactory.startBrowser("chrome", url);
LoginPage login=PageFactory.initElements(driver, LoginPage.class);
VerifyAddEmployeeTest verify_addEmployee=new VerifyAddEmployeeTest();
//LoginPageObjects loginPage = PageFactory.initElements(driver, LoginPageObjects.class);
//To verify that error message occur when username is wrong
#Test(priority=1)
public void verify2() throws InterruptedException
{
//LoginPage login=new LoginPage(driver);
Thread.sleep(2000);
login.typeUsername("adm");
login.typePassword("admin123");
login.clickLogin();
String actualError=login.findErrorMsg();
String expectedError="Invalid credentials";
Assert.assertEquals(actualError,expectedError);
}
//To verify that error message occur when password is wrong
#Test(priority=2)
public void verify3() throws InterruptedException
{
// LoginPage login=new LoginPage(driver);
Thread.sleep(2000);
login.typeUsername("admin");
login.typePassword("adm");
login.clickLogin();
String actualError=login.findErrorMsg();
String expectedError="Invalid credentials";
Assert.assertEquals(actualError,expectedError);
}
//To verify that error message occur both username and password are wrong
#Test(priority=3)
public void verify4() throws InterruptedException
{
// LoginPage login=new LoginPage(driver);
Thread.sleep(2000);
login.typeUsername("adm");
login.typePassword("adm");
login.clickLogin();
String actualError=login.findErrorMsg();
String expectedError="Invalid credentials";
Assert.assertEquals(actualError,expectedError);
}
//To verify that error message occur both username and password are empty
#Test(priority=4)
public void verify5() throws InterruptedException
{
// LoginPage login=new LoginPage(driver);
Thread.sleep(2000);
login.typeUsername("");
login.typePassword("");
login.clickLogin();
String actualError=login.findErrorMsg();
String expectedError="Username cannot be empty";
Assert.assertEquals(actualError,expectedError);
}
//To verify that error message occur password is empty
#Test(priority=5)
public void verify6() throws InterruptedException
{
// LoginPage login=new LoginPage(driver);
Thread.sleep(2000);
login.typeUsername("admin");
login.typePassword("");
login.clickLogin();
String actualError=login.findErrorMsg();
String expectedError="Password cannot be empty";
Assert.assertEquals(actualError,expectedError);
}
//To verify login is successful
#Test(priority=6)
public void verifyloginpage() throws Exception {
//login=new LoginPage( driver);
Thread.sleep(5000);
login.typeUsername("Admin");
login.typePassword("admin123");
login.clickLogin();
boolean actual_adminText=login.findDashBorad();
Assert.assertTrue(actual_adminText);
}
#Test(priority=7)
public void add_Employee()
{
verify_addEmployee.webPage(driver);
}
}
**
VerifyAddEmployeeTest.java
**
package OrangeTestCases.orangeHRM.Testcases;
import OrangeTestCases.orangeHRM.Pages.AddEmployee;
public class VerifyAddEmployeeTest {
AddEmployee add_emp;
//String url="https://opensource-demo.orangehrmlive.com/";
//WebDriver driver=BrowserFactory.startBrowser("chrome", url);
// WebDriver driver=BrowserFactory.getDriver();
// LoginPage login=PageFactory.initElements(driver, LoginPage.class);
void webPage(WebDriver driver) {
add_emp=PageFactory.initElements(driver, AddEmployee.class);
}
// to fill the registration form
#Test
public void verifyAddEmployeepage() {
// login = new LoginPage(driver);
// login.typeUsername("Admin");
// login.typePassword("admin123");
// login.clickLogin();
try {
System.out.println("value to object of driver sent successfully");
add_emp.clickPIM();
add_emp.clickAddEmployee();
add_emp.typefirstname("kumar");
add_emp.typemiddlename("sanu");
add_emp.typelastname("Singh");
add_emp.uploadPhoto("C:\\Users\\int120\\Desktop\\nw\\head2.png");
add_emp.clickCheckbox();
add_emp.typeUsername("kumar.sanu");
add_emp.typePassword("kumar_singh123");
add_emp.confirmPassword("kumar_singh123");
add_emp.selectStatus("Enabled");
add_emp.clickSavebtn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
So when I am running VerifyloginpageTest.java class it is successfully exexcuting all 6 login test cases and logging in the website but its not executing AddEmployee page.
According to TestNG documentation You should create testng.xml file if it is not there and after you should configure it as you want.
Example testng.xml file :
<suite name="TestExample">
<test name="Login">
<classes>
<class name="packageOfYourTest.VerifyloginpageTest" />
</classes>
</test>
<test name="AddEmployee">
<classes>
<class name="packageOfYourTests.AddEmployee" />
</classes>
</test>
</suite>
And Output will be like below :
testBeforeSuite()
testBeforeTest()
testLogin()
testAfterTest()
testBeforeTest()
testAddEmployee()
testAfterTest()
To make test classes run in parallel using TestNG, you should create testNG config xml file.
<suite name="SuiteName">
<test name="TestName">
<classes>
<class name="path.to.your.test.class.goes.here" />
</classes>
</test>
</suite>
Please note that using Before methods you actually create a thread and it's a good practice to create new instance of your driver for each thread.
Add parallel attribute to your config file on the 'suite' tag:
<suite name="SuiteName" parallel='classes'>
<test name="TestName">
<classes>
<class name="path.to.your.test.class.goes.here" />
</classes>
</test>
</suite>
You might parallel "methods", "tests" or "instances". But since you need run classes in parallel, use appropriate value.
Now, you can also add an attribute "thread-count" on the 'suite' tag as well to set max amount of classes which will run in parallel. In case you don't specify this attribute's value, amount will be equals 5 by default.
Now it's very important: whenever you call #Before TestNG creates new thread and you need to create driver for each thread and work with it within the exact thread. For doing this, you have to put every new driver instance into ThreadLocal<> container.
private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();
public static WebDriver getDriver() {
return DRIVER.get();
}
#BeforeMethod
public void setUp() {
if (DRIVER.get() == null) {
DRIVER.set(new FirefoxDriver());
}
}
#AfterMethod
public void tearDown() {
if (DRIVER.get() != null) {
DRIVER.remove();
}
}
Thus you will make your driver thread-safe. Also note that if you start driver in #BeforeClass, you need to driver.qiut() in #AfterClass. If start in #BeforeSuite - driver.qiut() in #AfterSuite and so on. To make sure everything works fine, you can print into your log thread id whenever you create or kill driver.
More info here:
http://testng.org/doc/documentation-main.html#parallel-running
https://www.swtestacademy.com/selenium-parallel-tests-grid-testng/
I have a java class that opens up two Chrome browsers, searches for "test 1" and "test 2", respectively. However, once both browsers open, only one browser with the google page will search for "test 1 test 2".
I believe this issue may be because I am calling the driver = new WebDriver from a parent class. However, I am not sure how to resolve the issue.
Here are my two methods that I am trying to run in parallel.
package webDrivertests;
public class googleTestClass extends Methods{
#Test
public void test1() throws InterruptedException {
googleTestClass object1;
object1 = new googleTestClass();
object1.launchBrowser();
object1.goToURL("https://www.google.com");
object1.enterValue("name","q","google test 1");
driver.quit();
}
#Test
public void test2() throws InterruptedException {
googleTestClass object2;
object2 = new googleTestClass();
object2.launchBrowser();
object2.goToURL("https://www.google.com");
object2.enterValue("name","q","google test 2");
driver.quit();
}
}
This is my xml file I use to call them.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="methods">
<test thread-count="2" name="Test" parallel="methods">
<classes>
<class name="webDrivertests.googleTestClass"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
The parent method that includes the driver
package webDrivertests;
// import statements
public class Methods {
public static WebDriver driver;
public void launchBrowser() {
System.setProperty("webdriver.chrome.driver","C:\\chromedriver_win32\\chromedriver.exe");
System.setProperty("webdriver.chrome.args", "--disable-logging");
System.setProperty("webdriver.chrome.silentOutput", "true");
driver = new ChromeDriver();
}
public void goToURL(String url) {
driver.get(url);
}
public void enterValue(String htmltype, String identifier, String value) throws InterruptedException {
if (htmltype == "id") {
WebElement element = driver.findElement(By.id(identifier));
element.clear();
element.sendKeys(value);
element.submit();
}
if (htmltype =="name") {
WebElement element = driver.findElement(By.name(identifier));
element.clear();
element.sendKeys(value);
element.submit();
}
Thread.sleep(3000);
}
}
Current Result: Two browsers are opened and each go to google.com. However only one browser will search for "test 1 test 2". Any help is appreciated! If possible, I would still like to use my parent class "Methods" as it contains a lot of methods I am using for my other real test cases.
Thanks in advance.
The problem lies in your test code. WebDriver object is being declared as a static object.
So this causes every test method to share the same instance.
To fix the problem remove the static keyword from the WebDriver declaration in your Methods class and try again.
Because of static declaration of the driver object in the class, it get's overridden parallel execution when second test called launchBrowser().
Obviously removing static will fix this issue but still you will fall in different issues of managing driver while your test bed and methods increase.
I would recommend to use any of TestNG extension that takes care of such requirement. We are using QAF which is built upon TestNG and provides driver management, resource management and many more features which are crucial for web/mobile/webservices testing.
I have a script setup developed using Appium and TestNG. The TestNG xml contains execution of multiple classes and each class has multiple test cases.
Example:
Class1:
-Class1_Test1
-Class1_Test2
-Class1_Test3
Class2:
-Class2_Test1
-Class2_Test2
-Class2_Test3
I tried integrating IRetryAnalyzer but that just calls the failed test case. The requirement is to execute the complete Class1 in case Class1_Test2 fails as soon as Class1 fails before it proceeds to Class2?
The reason for the ask is the app is a media player and if in case media playback fails due to network/server issues, next test cases of forward and rewind will not be required to be executed and it will need to relaunch the app and retry all steps before performing further tests.
There is no way to achieve this as per TestNg documentation, might be below answer can help you
Retry Logic - retry whole class if one tests fails - selenium
I am using the group test. It will continue the test even if some test fail in the class.
In your class file you can define group like following.
public class myClass(){
#Test(groups = {"abc"}, priority = 1)
public void test1(){
}
#Test(groups = {"abc"}, priority = 2)
public void test2(){
}
#Test(groups = {"abc"}, priority = 3)
public void test3(){
}
}
similarly you can define second class with same group name or different group name. Priority determines the order in which test case runs.
Your Testng.xml file will be look like following:
<test name="any name">
<groups>
<run>
<include name="abc" />
<include name="any other group name" />
</run>
</groups>
<classes>
<class name="packageName.myClass"/>
<class name="your_packageName.class2"/>
</classes>
</test>
packageName is the path where your Test class is located. If your test class and testng.xml files are in same package, packageName is not required.
For more information about test method in Testng refer this link.
Finally found a workaround to rerun the whole class. I would call it a workaround since technically TestNG does not provide a way to re-execute #BeforeTest in case a failure occurs at any point of time.
The best possible method I found was to have no #BeforeTest section and have just one #Test section and have all Test cases as functions which would be called within the single #Test defined. So in case a failure occured at any point of time, the #Test would be recalled which contains all the functions in the order required including setting up the capabilities. The retry logic reruns the entire #Test section as soon as a failure is observed.
Example:
Before the changes:
package <yourpackagename>;
<import required packages>
public class Home {
private AppiumDriver<?> driver;
private static final String url = "http://0.0.0.0:4723/wd/hub";
<define your variables>
#Parameters({"deviceOS", "DSN"})
#BeforeTest
public void setUp(String deviceOS, String DSN) throws InterruptedException, MalformedURLException, ParseException {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
capabilities.setCapability("deviceName", "FireTVStick");
capabilities.setCapability("platformVersion", deviceOS);
capabilities.setCapability("udid", DSN);
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("noReset", true);
capabilities.setCapability("fullReset", false);
capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 1200);
capabilities.setCapability("appPackage", "<your app package>");
capabilities.setCapability("appActivity", "<your launcher activity>");
driver = new AndroidDriver<>(new URL(url), capabilities);
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
//End of Launch Code
System.out.println("-Testing Home Section-");
}
#Parameters({"DSN"})
#Test
public void Test1_VideoPlaybackVerification(String DSN) throws InterruptedException, ParseException{
//Video playback verification code starts
.
.
//End of code for Video playback verification
}
#Test //Test Case for Pause verification
public void Test2_PauseVerification() throws InterruptedException, ParseException{
//Video pause verification code starts
.
.
//End of code for Video pause verification
}
#AfterTest
public void End() {
driver.quit();
}
}
After the changes:
package <yourpackagename>;
<import required packages>
#Listeners(MyTestListenerAdapter.class)
public class Home {
private AppiumDriver<?> driver;
<define your variables>
public void setUp(String port, String deviceOS, String DSN, String deviceName) throws InterruptedException, MalformedURLException {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
capabilities.setCapability("platformVersion", deviceOS);
capabilities.setCapability("deviceName", deviceName);
capabilities.setCapability("udid", DSN);
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("noReset", true);
capabilities.setCapability("fullReset", false);
capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 1200);
capabilities.setCapability("appPackage", "<your app package>");
capabilities.setCapability("appActivity", "<your launcher activity>");
final String url = "http://127.0.0.1:"+port+"/wd/hub";
driver = new AndroidDriver<>(new URL(url), capabilities);
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
}
public void HomeVerification(String DSN, String deviceName) throws InterruptedException, ParseException
{
System.out.println(deviceName+": Testing Home Section-");
<--Your code to perform any additional task before execution-->
}
public void Test1_VideoPlaybackVerification(String DSN, String deviceName) throws InterruptedException, ParseException
{
//Video playback verification code starts
.
.
//End of code for Video playback verification
}
public void Test2_PauseVerification(String deviceName) throws InterruptedException, ParseException
{
//Video pause verification code starts
.
.
//End of code for Video pause verification
}
#Parameters({"port", "deviceOS", "DSN", "deviceName"})
#Test (retryAnalyzer = Retry.class)
public void TestRun(String port, String deviceOS, String DSN, String deviceName) throws InterruptedException, ParseException, MalformedURLException {
try {
setUp(port, deviceOS, DSN, deviceName);
HomeVerification(DSN, deviceName);
Test1_VideoPlaybackVerification(DSN, deviceName);
Test2_PauseVerification(deviceName);
} catch (WebDriverException e) {
// TODO Auto-generated catch block
Reporter.log(deviceName+": Error observed while executing script!", true);
Assert.assertTrue(false); //Fails the test case
}
}
#AfterTest
public void End() {
driver.quit();
}
}
I need some help to figure out the problem while cross-browser selenium java test execution through the Saucelabs. I'm running one test over 2 browsers selected from the Jenkins job.
While the execution one test is passed another one (the similar) is getting failure with the error: 504 Gateway Time-out. The server didn't respond in time.
So it's unable to move into the next step.
It seems like one test interrupts another. Both tests are running under their own tunnel and thread.
Aug 30, 2018 7:17:44 AM org.openqa.selenium.remote.ProtocolHandshake
createSession
INFO: Detected dialect: OSS
30-08-2018 07:17:49.235 [TestNG-PoolService-0] INFO
[com.***.tests.TestBase_Local:96] - Open a site URLDriver: RemoteWebDriver:
chrome on XP (4f5a5d685f4c44c9a5864e91cb8f11e9)
Driver: RemoteWebDriver: chrome on XP (4f5a5d685f4c44c9a5864e91cb8f11e9)
thread id:14 Timestamp :2018-08-30T07:17:49.370
30-08-2018 07:17:51.912 [TestNG-PoolService-0] INFO
[com.**.tests.TestBase_Local:35] - Select 'No thanks' on the popup
Aug 30, 2018 7:17:53 AM org.openqa.selenium.remote.ProtocolHandshake
createSession
INFO: Detected dialect: OSS
30-08-2018 07:17:58.886 [TestNG-PoolService-1] INFO
[com.**.tests.TestBase_Local:96] - Open a site URLDriver:
RemoteWebDriver: MicrosoftEdge on ANY (c6978c03531d408485588ba501ff0589)
Driver: RemoteWebDriver: MicrosoftEdge on ANY
(c6978c03531d408485588ba501ff0589)
thread id:15 Timestamp :2018-08-30T07:17:58.887
30-08-2018 07:18:03.406 [TestNG-PoolService-1] INFO
[com.**.tests.TestBase_Local:35] - Select 'No thanks' on the popup
30-08-2018 07:18:05.337 [TestNG-PoolService-1] INFO
[com.**.tests.TestBase_Local:38] - Search by input
Sharing the code:
public class Search extends RemoteTestBase {
#Test(dataProvider = "browsers")
public void SolrSearchTest(String browser, String version, String os, Method method) throws Exception {
this.createRemoteDriver(browser, version, os, method.getName());
System.out.println("Driver: " + driver.toString());
Application app = new Application(driver);
ConfigFileReader configRead = new ConfigFileReader();
WebDriverWait wait = new WebDriverWait(driver,100);
app.homePage().SelectNoThanks();
Log.info("Select 'No thanks' on the popup");
app.searchField().SearchBy(configRead.SearchInput());
Log.info("Search by input");
}
}
The extended RemoteTestBase class:
public class RemoteTestBase {
public WebDriver driver;
private static String baseUrl;
RandomDataSelect randomuser;
private PropertyLoader propertyRead;
public Logger Log = Logger.getLogger(TestBase_Local.class.getName());
private static final String SAUCE_ACCESS_KEY = System.getenv("SAUCE_ACCESS_KEY");
private static final String SAUCE_USERNAME = System.getenv("SAUCE_USERNAME");
#BeforeMethod
#DataProvider(name = "browsers", parallel = true)
public static Object[][] sauceBrowserDataProvider(Method testMethod) throws JSONException {
String browsersJSONArrayString = System.getenv("SAUCE_ONDEMAND_BROWSERS");
System.out.println(browsersJSONArrayString);
JSONArray browsersJSONArrayObj = new JSONArray(browsersJSONArrayString);
Object[][] browserObjArray = new Object[browsersJSONArrayObj.length()][3];
for (int i=0; i < browsersJSONArrayObj.length(); i++) {
JSONObject browserObj = (JSONObject)browsersJSONArrayObj.getJSONObject(i);
browserObjArray[i] = new Object[]{ browserObj.getString("browser"), browserObj.getString("browser-version"), browserObj.getString("os")};
}
return browserObjArray;
}
void createRemoteDriver(String browser, String version, String os, String methodName) throws Exception {
DesiredCapabilities capabilities = new DesiredCapabilities();
Class<? extends RemoteTestBase> SLclass = this.getClass();
capabilities.setCapability("browserName", browser);
if (version != null) {
capabilities.setCapability("browser-version", version);
}
capabilities.setCapability("platform", os);
capabilities.setCapability("name", SLclass.getSimpleName());
capabilities.setCapability("tunnelIdentifier", "***");
driver = (new RemoteWebDriver(new URL("http://" + SAUCE_USERNAME + ":" + SAUCE_ACCESS_KEY + "#ondemand.saucelabs.com:80/wd/hub"), capabilities));
randomuser = new RandomDataSelect();
propertyRead = new PropertyLoader();
baseUrl = propertyRead.getProperty("site.url");
getURL();
}
private void getURL () {
driver.get(baseUrl);
driver.manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
this.annotate("Visiting HDSupply page..." + driver.toString());
Log.info("Open a site URL" + "Driver: " + driver.toString());
}
private void printSessionId() {
String message = String.format("SauceOnDemandSessionID=%1$s job-name=%2$s",(((RemoteWebDriver) driver).getSessionId()).toString(), "some job name");
System.out.println(message);
}
#AfterMethod(description = "Throw the test execution results into saucelabs")
public void tearDown(ITestResult result) throws Exception {
((JavascriptExecutor) driver).executeScript("sauce:job-result=" + (result.isSuccess() ? "passed" : "failed"));
printSessionId();
driver.quit();
}
void annotate(String text) {
((JavascriptExecutor) driver).executeScript("sauce:context=" + text);
}
}
The suite.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Tests Suite" verbose="4" parallel="tests" data-provider-thread-count="2">
<test name="AllTests" parallel="methods">
<classes>
<class name="com.***.tests.Search"/>
</classes>
</test>
</suite>
Project info: java, selenium, testng, maven, saucelabs, jenkins
The problem lies in your test code. Its definitely related to race condition between your #Test methods [ you have your parallel="true" in your #DataProvider annotation and parallel="methods" ]
You need to refactor your code such that your driver object is thread safe.
Some of the ways in which you can do it is using :
ThreadLocal variants of WebDriver.
Create your webdriver instance and inject that as an attribute into the ITestResult object.
The below sample shows how to use a ThreadLocal variant to make your code thread-safe
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
public class RemoteTestBase {
public static final ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<>();
private static String baseUrl;
private static final String SAUCE_ACCESS_KEY = System.getenv("SAUCE_ACCESS_KEY");
private static final String SAUCE_USERNAME = System.getenv("SAUCE_USERNAME");
void createRemoteDriver(String browser, String version, String os, String methodName)
throws Exception {
DesiredCapabilities capabilities = new DesiredCapabilities();
Class<? extends RemoteTestBase> SLclass = this.getClass();
capabilities.setCapability("browserName", browser);
if (version != null) {
capabilities.setCapability("browser-version", version);
}
capabilities.setCapability("platform", os);
capabilities.setCapability("name", SLclass.getSimpleName());
capabilities.setCapability("tunnelIdentifier", "***");
URL url = new URL(
"http://" +
SAUCE_USERNAME + ":" +
SAUCE_ACCESS_KEY + "#ondemand.saucelabs.com:80/wd/hub");
RemoteWebDriver rwd = new RemoteWebDriver(url, capabilities);
driver.set(rwd);
getURL();
}
protected static RemoteWebDriver getDriver() {
return driver.get();
}
private void getURL() {
getDriver().get(baseUrl);
getDriver().manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
this.annotate("Visiting HDSupply page..." + driver.toString());
}
private void printSessionId() {
String message = String.format("SauceOnDemandSessionID=%1$s job-name=%2$s",
getDriver().getSessionId(), "some job name");
System.out.println(message);
}
#AfterMethod(description = "Throw the test execution results into saucelabs")
public void tearDown(ITestResult result) {
String txt = "sauce:job-result=" + (result.isSuccess() ? "passed" : "failed");
getDriver().executeScript(txt);
printSessionId();
getDriver().quit();
}
void annotate(String text) {
getDriver().executeScript("sauce:context=" + text);
}
}
All your sub-classes would try to access the RemoteWebDriver object via getDriver() method.
The caveat is that your #BeforeMethod needs to call createRemoteDriver() so that the RemoteWebDriver object gets instantiated and pushed into the ThreadLocal context which will be valid and be accessible within a #Test method.
The rule is always #BeforeMethod (driver instantiation happens) > #Test (driver gets consumed > #AfterMethod (driver cleanup should happen). This is the only combo wherein a RemoteWebDriver object is valid within a ThreadLocal context.