Static variables in Javadoc is defined as:-
Sometimes, you want to have variables that are common to all objects. This is accomplished with the static
modifier. Fields that have the static
modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory. Any object can change the value of a class variable, but class variables can also be manipulated without creating an instance of the class.
Many Selenium WebDriver professionals use static class variables frequently and mostly make WebDriver a static reference variable. The advantages they get from static WebDriver is that they do not need to declare and initialize local WebDriver instances in multiple classes to keep same reference and can access WebDriver value from a single place. It also helps them to ignore NullPointerException for driver instance which is problem for many beginners in Selenium WebDriver. Whatever browser they launch, to use same reference without any problem they make WebDriver static. This post will use a static WebDriver reference variable and show how it creates a problem in running scripts in parallel but this applies for all static reference you use in your project.
Let’s see example codes with static WebDriver:-
I have created a class named “WebDriverFactoryStatic” where I have a static WebDriver reference and a static method “setDriver” which initialize a chrome browser i.e. initialize static WebDriver reference. Now you can use this static WebDriver reference using its class name i.e. WebDriverFactoryStatic. You could have WebDriver reference as private and put a getter method.
Class WebDriverFactoryStatic :-
package ParallelRunProblemWithStaticWebDriver; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import io.github.bonigarcia.wdm.WebDriverManager; public class WebDriverFactoryStatic { public static WebDriver driver; public static void setDriver() { WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(); } }
There are two simple TestNG classes with two @Test methods and a @BeforeClass and a @AfterClass configuration methods in each class. In @BeforeClass I am initializing browser so that both tests of a class runs on same browser. Clearing cookies as a last step in @Test methods. In @AfterClass method I am closing a browser. See the code below:-
Test Class TestOneWithStaticWebDriver :-
package ParallelRunProblemWithStaticWebDriver; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class TestOneWithStaticWebDriver{ @BeforeClass public void setUp() { System.out.println("Browser setup by Thread "+Thread.currentThread().getId()); WebDriverFactoryStatic.setDriver(); } @Test public void GoogleTest() throws InterruptedException { WebDriverFactoryStatic.driver.get("https://www.google.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @Test public void FacebookTest() throws InterruptedException { WebDriverFactoryStatic.driver.get("https://www.facebook.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @AfterClass public void tearDown() { System.out.println("Browser closed by Thread "+Thread.currentThread().getId()); WebDriverFactoryStatic.driver.close(); } }
Test Class TestTwoWithStaticWebDriver :-
package ParallelRunProblemWithStaticWebDriver; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class TestTwoWithStaticWebDriver { @BeforeClass public void setUp() { System.out.println("Browser setup by Thread "+Thread.currentThread().getId()); WebDriverFactoryStatic.setDriver(); } @Test public void FlipkartTest() throws InterruptedException { WebDriverFactoryStatic.driver.get("https://www.flipkart.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @Test public void MyntraTest() throws InterruptedException { WebDriverFactoryStatic.driver.get("https://www.myntra.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @AfterClass public void tearDown() { System.out.println("Browser closed by Thread "+Thread.currentThread().getId()); WebDriverFactoryStatic.driver.close(); } }
Let’s run above TestNG classes linearly using TestNG xml as below:-
Output:–
// TestOneWithStaticWebDriver tests Browser setup by Thread 1 Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 41733 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Title printed by Thread 1 - Google Title printed by Thread 1 - Facebook – log in or sign up Browser closed by Thread 1 // TestTwoWithStaticWebDriver Browser setup by Thread 1 Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 29005 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Title printed by Thread 1 - Online Shopping Site for Mobiles, Electronics, Furniture, Grocery, Lifestyle, Books & More. Best Offers! Title printed by Thread 1 - Online Shopping for Women, Men, Kids Fashion & Lifestyle - Myntra Browser closed by Thread 1 =============================================== Suite Total tests run: 4, Passes: 4, Failures: 0, Skips: 0 ===============================================
We can see tests were run one by one successfully using one thread in linear execution. Execution started from TestOneWithStaticWebDriver class and a browser was initialized in BeforeClass method. Both tests of this class was executed one after another. After executing test methods initialized browser was closed in AfterClass method. Similar sequence of execution was done for TestTwoWithStaticWebDriver class.
Time is money. Let’s save time and run tests in parallel. Let’s run TestNG classes in parallel with thread count as 2. Two different threads will be executing two TestNG classes.
TestNG XML with parallel setup:-
Output:-
Browser setup by Thread 12 Browser setup by Thread 11 Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 9473 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 46868 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Title printed by Thread 11 - Google Title printed by Thread 12 - Facebook – log in or sign up Title printed by Thread 11 - Online Shopping for Women, Men, Kids Fashion & Lifestyle - Myntra Browser closed by Thread 11 Browser closed by Thread 12 =============================================== Suite Total tests run: 4, Passes: 3, Failures: 1, Skips: 0 Configuration Failures: 1, Skips: 0 ===============================================
We were expecting that one thread will execute all @Test methods of first TestNG class and second thread will execute all @Test methods of another TestNG class. But you can see in above output it did not happen in expected way. You see test method and configuration method failures If you rerun you may see different output with a open browser left. This happened because of static WebDriver. Static WebDriver reference was shared by both threads.
First thread initialized a browser and set a value to static WebDriver reference. Second thread initialized another browser and set a new value to the same static WebDriver reference and this will impact value set by first thread as it is a static. If you are confused with static concept in Java please refer Javadoc here.
Two threads call “setDriver” method of WebDriverFactoryStatic class simultaneously but both uses same static reference of WebDriver as it is static. Thread one sets value of driver as X which was reset by Thread second as Y. Now both threads will have driver reference as Y only and X is abandoned now. Both threads wanted to close same browser that is the reason there is one configuration method failure as once closed another thread will not find session. Browser was closed already so last test did not get a browser for execution.
Once second thread sets final value for static WebDriver, both threads started working on same browser itself. Another browser is left unattended. If you observe output carefully, you will find tests for printing title of loaded URLs is also not from same class. A thread should have loaded Google and Facebook and another thread should have loaded Flipkart and Myntra but it was messed because of static reference. Both threads worked on same instance.
Okay, you don’t believe on my words how execution went wrong. Let’s print WebDriver reference in tests and observe output:-
TestOneWithStaticWebDriver:–
package ParallelRunProblemWithStaticWebDriver; import java.lang.reflect.Method; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class TestOneWithStaticWebDriver{ @BeforeClass public void setUp() { WebDriverFactoryStatic.setDriver(); System.out.println("Browser setup by Thread "+Thread.currentThread().getId()+" and Driver reference is : "+WebDriverFactoryStatic.driver); } @Test public void GoogleTest(Method m) throws InterruptedException { System.out.println(m.getName()+" of class TestOneWithStaticWebDriver Executed by Thread "+Thread.currentThread().getId()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.get("https://www.google.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @Test public void FacebookTest(Method m) throws InterruptedException { System.out.println(m.getName()+" of class TestOneWithStaticWebDriver Executed by Thread "+Thread.currentThread().getId()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.get("https://www.facebook.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @AfterClass public void tearDown() { System.out.println("Browser closed by Thread "+Thread.currentThread().getId() + " and Closing driver reference is :"+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.close(); } }
TestTwoWithStaticWebDriver :-
package ParallelRunProblemWithStaticWebDriver; import java.lang.reflect.Method; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class TestTwoWithStaticWebDriver { @BeforeClass public void setUp() throws InterruptedException { WebDriverFactoryStatic.setDriver(); System.out.println("Browser setup by Thread "+Thread.currentThread().getId()+" and Driver reference is : "+WebDriverFactoryStatic.driver); } @Test public void FlipkartTest(Method m) throws InterruptedException { System.out.println(m.getName()+" of class TestOneWithStaticWebDriver Executed by Thread "+Thread.currentThread().getId()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.get("https://www.flipkart.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @Test public void MyntraTest(Method m) throws InterruptedException { System.out.println(m.getName()+" of class TestOneWithStaticWebDriver Executed by Thread "+Thread.currentThread().getId()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.get("https://www.myntra.com/"); Thread.sleep(15000); System.out.println("Title printed by Thread "+Thread.currentThread().getId()+" - "+WebDriverFactoryStatic.driver.getTitle()+" on driver reference "+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.manage().deleteAllCookies(); } @AfterClass public void tearDown() { System.out.println("Browser closed by Thread "+Thread.currentThread().getId() + " and Closing driver reference is :"+WebDriverFactoryStatic.driver); WebDriverFactoryStatic.driver.close(); } }
Output:-
Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 16784 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 8606 Only local connections are allowed. Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code. Browser setup by Thread 11 and Driver reference is : ChromeDriver: chrome on WINDOWS (cb4a8a79ab3989dcf478bb4532774127) FacebookTest of class TestOneWithStaticWebDriver Executed by Thread 11 on driver reference ChromeDriver: chrome on WINDOWS (cb4a8a79ab3989dcf478bb4532774127) Browser setup by Thread 12 and Driver reference is : ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) FlipkartTest of class TestOneWithStaticWebDriver Executed by Thread 12 on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) Title printed by Thread 12 - Online Shopping Site for Mobiles, Electronics, Furniture, Grocery, Lifestyle, Books & More. Best Offers! on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) MyntraTest of class TestOneWithStaticWebDriver Executed by Thread 12 on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) Title printed by Thread 11 - Online Shopping for Women, Men, Kids Fashion & Lifestyle - Myntra on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) GoogleTest of class TestOneWithStaticWebDriver Executed by Thread 11 on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) Title printed by Thread 12 - Google on driver reference ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) Browser closed by Thread 12 and Closing driver reference is :ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) Browser closed by Thread 11 and Closing driver reference is :ChromeDriver: chrome on WINDOWS (3762c9484a5b94a077f7b7371fa6ead4) =============================================== Suite Total tests run: 4, Passes: 3, Failures: 1, Skips: 0 Configuration Failures: 1, Skips: 0 ===============================================
Now you see the printed driver reference. First initialization of driver reference was overwritten by second initialization of driver reference and both threads started working on same browser instance.
We will see solution of above problem in next post.
You can download code form this repo.
If you have any doubt, feel free to comment below.
If you like my posts, please like, comment, share and subscribe.
#ThanksForReading
#HappyLearning
You can find all Selenium related post here.
You can find all API manual and automation related posts here.
You can find frequently asked Java Programs here.
Hi Amod
We are in similar problem with “static” webdriver in our framework. But we are using Junit instead of testNG. Recently we upgraded to Junit5 as we understood it supports parallelism. But the parallel executions are not working due to usage of static WebDriver reference across our existing framework. Could this “Thread Local” usage help our issue? Please help us in this regard.
Cheers
Nagarjun K S
Im getting no issues when I’m running this example and not using webdriver as static, so why should I use a static webdriver and make things complicated! And lets say I can declare my webdriver instance at before annotation of every test class. Declaring instances in every test class seems little clumpsy , but it would also reduce my effort to make threadlocal stuffs. So why should I follow that approach?
Hi Please can you share the solution for the above problem
http://makeseleniumeasy.com/2020/05/27/threadlocal-static-webdriver-for-parallel-execution/
Very detail , thank you so much.
A good detailed post.