Selenium test is overwritten by the second session while the parallel execution - java

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.

Related

How to reuse the drivers in testNG parallel execution

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
}

Selenium, TestNg parallel run not working as expected

I have 3 test classes consisting of multiple test methods that I want to run in parallel. I'm using ThreadLocal for isolating webdriver instances per thread. When I run the tests in sequential manner everything looks fine but problem arises when I run them in parallel. Below is my suite file
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="platform" parallel="classes" thread-count="5">
<test name="platform">
<classes>
<class name="com.sat.platform.mobile.PlatformMobileIdCaptureMonitoringWf11"></class>
<class name="com.sat.platform.mobile.PlatformMobileIdVerificationMonitoringWf2"></class>
<class name="com.sat.platform.mobile.PlatformMobileIdandIVMonitoringWf3"></class>
<class name="com.sat.platform.mobile.PlatformMobileLivenessMonitoringWf6"></class>
<class name="com.sat.platform.mixed.PlatformMixedIdSimilarityMonitoringWf2and5"></class>
</classes>
</test>
</suite>
I'm initializing Webdriver in #BeforeClass in BrowserClient.java as below.
protected WebDriver driver;
private static int implicitWaitTime;
private static int explicitWaitTime;
private static int fluentWaitTime;
private static int pollingTime;
protected static WebDriverWait explicitWait;
protected static Wait<WebDriver> fluentWait;
private static String browser;
protected static Browsers browsers;
static {
Properties prop = new Properties();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream stream = loader.getResourceAsStream("browser.properties");
try {
prop.load(stream);
} catch (IOException e) {
}
implicitWaitTime = Integer.parseInt(prop.getProperty("browser.implicit.wait.timeout"));
explicitWaitTime = Integer.parseInt(prop.getProperty("browser.explicit.wait.timeout"));
fluentWaitTime = Integer.parseInt(prop.getProperty("browser.fluent.wait.timeout"));
pollingTime = Integer.parseInt(prop.getProperty("browser.wait.polling.time"));
browser = System.getProperty("browser");
}
#BeforeClass
public void initializeEnv() throws MalformedURLException {
driver = BrowserFactory.createInstance(browser, implicitWaitTime);
DriverFactory.getInstance().setDriver(driver);
driver = DriverFactory.getInstance().getDriver();
explicitWait = new WebDriverWait(driver, explicitWaitTime);
fluentWait = new FluentWait(driver).withTimeout(Duration.of(fluentWaitTime, SECONDS))
.pollingEvery(Duration.of(pollingTime, SECONDS))
.ignoring(NoSuchElementException.class);
}
the used class here i.e BrowserFactory.java
public static WebDriver createInstance(String browser, int implicitWaitTime) throws MalformedURLException {
WebDriver driver = null;
Browsers browserEnum = Browsers.valueOf(browser);
String testVideo = ImageProcessingUtils.getAbsolutePath("digital_copy.mjpeg", false);
switch (browserEnum) {
case chrome:
ChromeOptions options = new ChromeOptions();
options.addArguments("--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream",
"--use-file-for-fake-video-capture=" + testVideo, "--start-maximized");
driver = new ChromeDriver(options);
break;
case firefox:
FirefoxProfile firefoxProfile = new FirefoxProfile();
firefoxProfile.setPreference("media.navigator.permission.disabled", true);
firefoxProfile.setPreference("media.navigator.streams.fake", true);
firefoxProfile .setPreference("browser.private.browsing.autostart", false);
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setProfile(firefoxProfile);
driver = new FirefoxDriver(firefoxOptions);
break;
}
}
DriverFactory.java
public class DriverFactory {
private static DriverFactory instance = new DriverFactory();
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
private static List<WebDriver> driversList = new ArrayList();
private DriverFactory(){
}
public static DriverFactory getInstance() {
return instance;
}
public WebDriver getDriver(){
WebDriver localDriver = driver.get();
driversList.add(localDriver);
return localDriver;
}
public void setDriver(WebDriver driver){
this.driver.set(driver);
}
public static void removeDriver(){
for(WebDriver driver : driversList) {
driver.quit();
}
}
}
And my test classes extends BrowserClient.java where i can use the driver directly. One of the common method in all 3 test classes is merchant_gets_oauth_token() as shown below. The problem is when test suite is run, 3 firefox browsers are open in parallel and all of them navigates to the login page but only 1 and sometimes 2 of the tests passes while the 3rd one fails (unable to login).
#Test
public void merchant_gets_oauth_token() {
OAuth2Client client = new OAuth2Client();
String loginUrl = DslConfigFactory.getEnvConfig("portal.customer.url");
driver.get(loginUrl);
CustomerPortalLoginPage loginPage = new CustomerPortalLoginPage(driver);
log.info("----------merchant logging to customer portal to get oauth token----------");
loginPage.login(merchantUser.getEmail(), merchantUser.getPassword());
CustomerPortalHomePage homePage = new CustomerPortalHomePage(driver);
homePage.clickOnSettings();
CustomerPortalSettingsPage settingsPage = new CustomerPortalSettingsPage(driver);
settingsPage.clickOnApiCredentials();
CustomerPortalApiCredentialsPage apiCredentialsPage = new CustomerPortalApiCredentialsPage(driver);
clientCredentials = apiCredentialsPage.getOauth2ClientCredentials();
oauthToken = client.getOauthToken(clientCredentials.get("token"), clientCredentials.get("secret"));
}
I have been struggling with this problem for a while and had looked up lot of online resources without help. Maybe somebody here is able to do the RCA. Thanks in Advance!!
public static void removeDriver(){
for(WebDriver driver : driversList) {
driver.quit();
}
}
Here, replace driver.quit() with driver.close().
driver.close() method it will close only current driver. driver.close() closes all drivers.
When first instance executes removeDriver(), it will closes all windows/drivers. Then next instance try to execute removeDriver(),there driver is not available because it is already closed by previous instance of driver.so you get error.

TestNG Selenium: Why does #Before work fine but #BeforeTest throws a NullPointerException?

This is my step definition class which is failing, the first #Test where the driver gets the URL is throwing a NullPointerException:
public class stepDefinitionASOS extends base {
AsosElements ae;
#BeforeTest
public void initializeTest() throws IOException {
driver = initializeDriver();
PageFactory.initElements(driver, this);
driver.manage().window().maximize();
ae = new AsosElements(driver);
}
#Test
#Given("^User goes to the \"([^\"]*)\" website$")
public void user_navigates_to_the_ASOS_website(String url) throws Throwable {
driver.get(url);
}
#Test
#Given("^User navigates to the account page$")
public void user_navigates_to_the_account_page() throws Throwable {
ae.clickAsosMyAccountDropdown(driver);
ae.clickAsosMyAccountButton(driver);
}
#Test
#When("^User logs in with (.+) and (.+)$")
public void user_logs_in_with_username_and_password(String username, String password) throws Throwable {
ae.typeEmailAddress(username);
ae.typePassword(password);
ae.clickSignInButton();
}
#Test
#Then("^Login should be successful$")
public void login_successful_is_something() throws Throwable {
Assert.assertTrue(ae.getMyAccountTitle().isDisplayed());
}
#AfterTest
public void teardown() {
driver.close();
driver = null;
}
}
The tests seem to run fine when I use the #Before Cucumber annotation instead of #BeforeTest TestNG annotation.
This is the base class that the step definition class is inheriting from:
public class base {
public WebDriver driver;
public Properties prop;
public static final String USERNAME = ""; // I have blanked this out for security reasons
public static final String AUTOMATE_KEY = ""; // I have blanked this out for security reasons
public static final String URL = "https://" + USERNAME + ":" + AUTOMATE_KEY + "#hub-cloud.browserstack.com/wd/hub";
public WebDriver initializeDriver() throws IOException {
DesiredCapabilities caps = new DesiredCapabilities();
DesiredCapabilities capability = new DesiredCapabilities();
caps.setCapability("os", "OS X");
caps.setCapability("os_version", "High Sierra");
caps.setCapability("browser", "Firefox");
caps.setCapability("browser_version", "54.0");
caps.setCapability("browserstack.local", "false");
caps.setCapability("browserstack.selenium_version", "3.5.2");
driver = new RemoteWebDriver(new URL(URL), caps);
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
return driver;
}
Feature file:
#AsosLogin
Feature: Logging into ASOS
Scenario Outline: Going to ASOS website and logging in correct details
Given User goes to the "https://www.asos.com" website
And User navigates to the account page
When User logs in with <username> and <password>
Then Login should be successful
Examples:
|username |password |
|random#hotmail.co.uk |password |
StackTrace:
java.lang.NullPointerException at
stepDefinitions.stepDefinitionASOS.user_navigates_to_the_ASOS_website(stepDefinitionASOS.java:24)
at ✽.Given User goes to the "https://www.asos.com"
website(C:/Users/aliba/Documents/googleTingYaKnaDisOne/src/test/java/features/asosLogin.feature:5)
Does anyone know how I can go about fixing this?

Inject WebDrivers in TestNG #before test with #guice

I am new with configure selenium. Looked for passing drivers find this solution https://stackoverflow.com/a/35101914/7104440 I wonder is possible to #inject many drivers from browsers in this way. Is possible to bind different drivers? I got error with this code:
encom.google.inject.CreationException: Unable to create injector, see the following errors:
1) Binding to null instances is not allowed. Use toProvider(Providers.of(null)) if this is your intended behaviour.
at assecobs.driver.DriverModule.configure(DriverModule.java:31)
2) A binding to org.openqa.selenium.WebDriver was already configured at assecobs.driver.DriverModule.configure(DriverModule.java:31).
at assecobs.driver.DriverModule.configure(DriverModule.java:31)
DriverModule.class
private DriverSetup driverSetup = new DriverSetup();
#BeforeSuite
#Override
public void configure(Binder binder) {
for (BrowserNames browserName : BrowserNames.values()) {
System.out.println(" bind " + browserName.toString());
WebDriver driver = driverSetup.initDriver(browserName.toString());
binder.bind(WebDriver.class).toInstance(driver);
}
}
}
DriverSetup.class
#SneakyThrows
public WebDriver initDriver(String browser) {
if (browser.equalsIgnoreCase("chrome")) {
capabilities = chromeCapabilities();
driver = initChromeDriver(capabilities);
} else if (browser.equalsIgnoreCase("firefox")) {
capabilities = firefoxCapabilities();
driver = initFirefoxDriver(capabilities);
} else if (browser.equalsIgnoreCase("opera")) {
capabilities = operaCapabilities();
driver = initOperaDriver(capabilities);
} else {
capabilities = firefoxCapabilities();
return driver = initFirefoxDriver(capabilities);
}
return driver;
}
ClientTest.class
#Guice(modules = {DriverModule.class})
public class ClientTest extends DriverSetup {
#Inject
WebDriver driver;
I have been using Guice + WebDriver for a while. You can inject webdriver as you have shown in the ClientTest.java.
Check here for the detailed steps. - http://www.testautomationguru.com/selenium-webdriver-dependency-injection-using-guice/

Webdriver get Item From Local Storage

I'm trying to get an item from local storage using Selenium Webdriver.
I followed this site but when I run my code I get NullPointerException.
When I debug the code I see the function: getItemFromLocalStorage returns NULL for some reason.
Here is my code:
public class storage
{
public static WebDriver driver;
public static JavascriptExecutor js;
public static void main(String[] args)
{
System.setProperty("webdriver.chrome.driver", "D://chromedriver.exe");
driver = new ChromeDriver();
driver.get("http://html5demos.com/storage");
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.findElement(By.id("local")).sendKeys("myLocal");
driver.findElement(By.id("session")).sendKeys("mySession");
driver.findElement(By.tagName("code")).click(); // just to escape textbox
String sItem = getItemFromLocalStorage("value");
System.out.println(sItem);
}
public static String getItemFromLocalStorage(String key)
{
return (String) js.executeScript(String.format(
"return window.localStorage.getItem('%s');", key));
}
}
That is because you forgot to instantiate js object correctly. Add below line after driver = new ChromeDriver();.
js = ((JavascriptExecutor)driver);
It will work.
I assume you have NPE on your driver instance.
You can setup driver location property while driver instance creating:
final ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()
.usingDriverExecutable(new File("D://chromedriver.exe")).build();
driver = new ChromeDriver(chromeDriverService);
BTW, I used selenium 2.44.0
Use this Code
WebStorage webStorage = (WebStorage) new Augmenter().augment(driver);
LocalStorage localStorage = webStorage.getLocalStorage();
String user_data_remember = localStorage.getItem("user_data_remember");
String emailAfterLogout;
String passwordAfterLogout;
if (!user_data_remember.equals("")) {
JSONObject jsonObject = new JSONObject(user_data_remember);
Boolean remember = jsonObject.getBoolean("remember");
if (remember) {
emailAfterLogout = jsonObject.getString("email");
passwordAfterLogout = jsonObject.getString("password");
if (emailAfterLogout.equals(email) && passwordAfterLogout.equals(password)) {
System.out.println("Remember me is working properly.");
} else {
System.out.println("Remember me is not working.");
}
}
} else {
System.out.println("Remember me checkbox is not clicked.");
}

Categories

Resources