Using driver.quit() in parallel run - selenium-webdriver

I am running test in parallel using parallel="classes". Till now i was closing browser using driver.close() in #AfterClass. Once test is run, window would close.
public class DriverFactory {
public WebDriver getDriver() {
return setupChromeDriver();
}
}
public class Base {
private final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
#BeforeClass
public void initialise() {
driver.set(DriverFactory.getInstance().getDriver());
}
public WebDriver getDriver() {
return driver.get();
}
#AfterClass
public void closeBrowser() {
driver.get().close();
driver.get().remove();
}
}
Recently i realised close() does not end driver session which caused many chromedriver and geckodriverinstances to stay in memory after test suite is run. I had to kill manually using killall chromedriver.
Solution :
I tried with driver.quit() in #AfterClass, which worked fine and killed session nicely but it worked for test which are not running in parallel.
Challenge:
If i use quit() for parallel run, it closes all running windows. If i use it in #AfterSuite, it will work but it would not be ideal to leave all browser windows open until complete test suite is run . I have around 200 tests.
If i use close() in #AfterClass and use quit() in #AfterSuite, it gives no such session exception because driver is closed which is correct.
Chrome version : 83.0.4103.61
Chromedriver version : 83.0.4103.39
Selenium version : 3.141.59
Any help is appreciated here, thanks .

Related

How to handle browser invocation in parallel tests using TestNG

I have been using browser invocation in #Beforeclass TestNG method using the parameter passed from testng.xml. Once the browser is invoked, I am using login test in a #BeforeMethod which is required for all the other #Tests to start. But with this setup, I am unable to run the tests in parallel. I am seeing one browser is open and login tests which is required by both tests are run in same browser and fails. All my tests are in single class file. Code structure is as below:
public class MainTest {
WebDriver driver;
BrowserFactory browser;
ReadData crmdatafile;
#BeforeClass(alwaysRun = true)
public void setup(ITestContext context) throws Exception{
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("datafile.txt").getFile());
crmdatafile = new ReadData(file.getAbsolutePath());
browser = new BrowserFactory(context.getCurrentXmlTest().getParameter("Selenium.browser"));
driver = browser.getNewBrowser();
driver.manage().deleteAllCookies();
driver.get(crmdatafile.data.get("enviornment", "url"));
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
}
#BeforeMethod()
public void login(){
System.out.println("contains the code for login which needs to be run before every test");
}
#AfterMethod
public void afterEachTest(ITestResult result) throws IOException {
driver.close();
}
#Test
public void Test1() {
System.out.println("Will run login and next steps");
}
#Test
public void Test2() {
System.out.println("Will run login and next steps");
}
public class BrowserFactory {
private WebDriver driver;
private String browser;
public BrowserFactory(String browser) {
String brow=browser.trim().toLowerCase();
if(!(brow.equals("firefox") || brow.equals("ff") || brow.equals("internetexplorer") || brow.equals("ie") || brow.equals("chrome") || brow.equals("gc"))) {
browser="ie";
}
this.browser = browser;
}
public WebDriver getNewBrowser() {
String brow = browser.trim().toLowerCase();
if(brow.equals("firefox") || brow.equals("ff")) {
System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability("marionette", true);
driver = new FirefoxDriver(capabilities);
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
return driver;
}else if(brow.equals("internetexplorer") || brow.equals("ie")){
driver = new InternetExplorerDriver();
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
return driver;
}else if(brow.equals("chrome") || brow.equals("gc")){
System.setProperty("webdriver.chrome.driver", "drivers\\chromedriver.exe");
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
options.addArguments("test-type");
capabilities.setCapability("webdriver.chrome.binary","drivers\\chromedriver.exe");
capabilities.setCapability(ChromeOptions.CAPABILITY, options);
driver = new ChromeDriver(capabilities);
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
return driver;
}
return null;
}
How can I improve the above structure to run tests in parallel and using a different browser for each test?
Your test code has some issues.
You are instantiating a webdriver instance in your #BeforeClass annotated method and that is being shared by all your #Test annotated test methods. You are also invoking driver.close() in an #AfterMethod annotated method.This is a recipe for disaster because AFAIK driver.close() is equivalent to driver.quit() if you are having only one web browser instance. So what this means is that your second test method will not have a valid browser (if you are running sequentially).
If you are running in parallel, then all your test methods are going to be competing for the same browser, which is now going to cause race conditions and test failures.
You should ideally speaking move the logic of your setup() method into a listener which implements IInvokedMethodListener interface and then have this listener inject the webdriver instances into a ThreadLocal variable, which your #Test methods can then query.
You can refer to my blog post which talks in detail on how to do this.
Please check if that helps.
Here is the thing, as far as I can see you're not using annotations right and (probaly) testNG as well.
If you want to create browser instance before each test you may want to use #BeforeTest annotation. Also you may perform your test set up in this method as well. #BeforeClass annotation will be executed only once before this particular class will be executed.
It's a good idea to split tests that do different things into separate test files, case your tests do almost the same, but say params for tests are different - it's a good idea to use #DataProvider. Otherwise maybe you may want to extend your basic test with setup.
If you just want to re-execute same tests, but with different browser - consider relaunching your test job with different params or using #DataProvider as described above.
As far as I remember there are several ways to run testNG in parallel: Methods, Classes, Tests, Instances - figure out which one you need and which one works better with your setup.

When I run a test suite with ChromeDrive Chrome is opened at every test

I'm trying to use the same code with Appium driver and Java , TestNG but with ChromeDriver I changed the configuration by adding that code :
File file = new File("C:/QA/Emna/chromedriver.exe");
System.setProperty("webdriver.chrome.driver", file.getAbsolutePath());
WebDriver driver = new ChromeDriver();
The problem is that any test case the chrome window is opened a new one, even if my tests are in a correct order with Priority (by TestNG).
Is there a way to work in only one window?
WebDriver driver = new ChromeDriver(); is what opens a new browser each time. Move it to #BeforeClass section and use the same instance in all the tests.
You need to move you code snippet in any #Before type of TestNG methods. Lets say your all test cases are in a TestNG class then
Here is how it should look like:
#BeforeClass
public void deSetup(){
File file = new File("C:/QA/Emna/chromedriver.exe");
System.setProperty("webdriver.chrome.driver", file.getAbsolutePath());
WebDriver driver = new ChromeDriver();
}
But if this is not the case, what I mean by this, that your test cases are spread across the multiple TestNG classes then the best way is to have a Singleton class to load and initialize the ChromeDriver. Call method of the Singleton class to instantiate the ChromeDriver in any one of method annotated as #BeforeSuite #BeforeTest or #BeforeGroups. And have a reference variable of WebDriver type in evelry test class and assigne the previously initialized ChromeDriver to this reference variable in #BeforeClass method.
In such a way, ChromeDriver will be instantiated only once when any one of #BeforeSuite #BeforeTest or #BeforeGroups method runs and will be available in every TestNG class once #BeforeClass runs.
This way you can work in only one Chrome window.
I have fixed my problem by this way in #BeforeClass:
#BeforeClass
public static void before() {
// check active session
System.out.println("Becore Test Method");
File file = new File("C:/QA/Emna/chromedriver.exe");
System.setProperty("webdriver.chrome.driver", file.getAbsolutePath());
wd = new ChromeDriver();
}
I made an instance like that:
static WebDriver driver ;
Then in every test I put the following :
#Test
public static void run1() {
//my tests using wd
}
#Test
public static void run2() {
//my tests using wd
}

NUnit will not run consecutive Selenium Webdriver C# tests

Selenium Webdriver 2.48 , C#, NUnit 2.6.4, Chrome Driver
When I run my tests from the NUnit test runner, they all pass if run individually.
If I select a main heading node, and select "Run", the first test in the group will run, the rest will fail.
If I have the test fixture [TearDown] close the driver at the end of each test, the following error occurs:
"Invalid OPeration Exception: No such session"
If I have the test fixture [TearDown] quit the driver, the following error occurs:
"Unexpected error. System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:13806
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)"
Using either driver.Quit() or driver.Close() makes no difference to the result - only the first test in the group running.
I have searched but not been able to find a solution. It must be possible to run all tests by running from the top-most node, rather than having to select each test and run them individually. Any help would be appreciated. Thanks. Michael
Here is an example which has two tests in the one class. I have removed most of the methods from the tests as they are very long.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium;
using NUnit.Framework;
using SiteCore.HousingRepairsLogic;
namespace SiteCore.HousingRepairsTests.DoorsAndWindowsTests
{
[TestFixture]
class DoorsTests
{
private IWebDriver driver = new ChromeDriver(#"C:\chromedriver_win32");
[SetUp]
public void setup()
{
HousingRepairsLogic.Utilities utilities = new Utilities(driver);
utilities.NavigateToLogin();
}
[TearDown]
public void teardown()
{
Utilities utilities = new Utilities(driver);
utilities.CloseDriver();
}
[Test]
public void LockRepair()
{
//Create Instance of the HomePage class
HomePage homepage = new HomePage(driver);
homepage.ClickHousingButton();
homepage.RequestRepairButton();
homepage.RequestRepairNowButton();
}
[Test]
public void ExternalWoodDoorFrameDamaged()
{
//Create Instance of the HomePage class
HomePage homepage = new HomePage(driver);
homepage.ClickHousingButton();
homepage.RequestRepairButton();
homepage.RequestRepairNowButton();
//Create instance of TenancyPage class
TenancyPage tenancy = new TenancyPage(driver);
//proceed with login
tenancy.ClickYesLoginButton();
//enter username
tenancy.EnterMyeAccountUserName();
//enter password
tenancy.EnterMyeAccountPassword();
//click the login button
tenancy.ClickLoginButton();
}
}
You initialize the driver once in the fixture, when it is declared:
private IWebDriver driver = new ChromeDriver(#"C:\chromedriver_win32");
Then your first test runs, uses the driver and the teardown closes it. The next test no longer has use of the driver.
You need to either: reinitialize the driver in the setup, or you need to close it in the fixture teardown.
If you chose to initialise and close in the setup/teardown you will see that the driver starts a new browsersession for every test. This will make sure that your tests are independant of eachother, but it will cost a lot more runtime.
If you want to re use the browsersession for all the tests: move the initialization and closure to TestFixture Setup and TestFixture Teardown methods.

JBehave #BeforeStory webdriver usage

I'm using the JBehave tutorial (Spring) https://github.com/jbehave/jbehave-tutorial with just 1 change, replacing PerStoryWebDriverSteps to PerStoriesWebDriverSteps.
When I try to use webDriver inside #BeforeStory I have this error message:
org.jbehave.web.selenium.DelegatingWebDriverProvider$DelegateWebDriverNotFound: WebDriver has not been found for this thread.
With this step:
public class LifecycleSteps {
private final WebDriverProvider webDriverProvider;
public LifecycleSteps(WebDriverProvider webDriverProvider) {
this.webDriverProvider = webDriverProvider;
}
#BeforeStory
public void test() {
webDriverProvider.get();
}
...
}
I just want to know if it's a bug, a misconfiguration or not possible to use webDriver in #BeforeStory?
In past I used Behat/Mink (PHP BDD) and It was not possible to use webDriver inside #BeforeStory because webDriver was not load. But in Jbehave you can select the webDriver init by using PerXXXXWebDriverSteps. And I thought it was possible to use webDriver in #BeforeStory if webDriver is init in #BeforeStories (using PerStoriesWebDriverSteps)

How can I run Fluentlenium Code inside Selenium Webdriver Firefox Driver?

I am having issues trying to get my Fluentlenium code to run inside the WebDriver Firefox Driver. I need Fluentlenium to execute inside the WebDriver Firefox Driver instead of opening it's own browser. I think I need to override this but I am not exactly sure how to do this. Any help would greatly appreciated. Thanks! Here is what I have for code:
WebDriver driver = new FirefoxDriver();
#Test
public void create_a_picklist()
{
// Go to Page
goTo("http://www.google.com");
}
What happens is that it opens two browsers. One is from the Firefox Driver and the other must be the default browser from the goTo from Fluentlenium. I need it to run this code inside the Firefox Driver window and not open it's own window from Fluentlenium.
By default, it launchs a Firefox browser so that's sufficient :
public class Test extends FluentTest {
#Test
public void go_to_google()
{
goTo("http://www.google.com");
}
}
And nothing more :)
Ok. Looks like I figured it out. Here is what I did to override the browser:
public class Test extends FluentTest {
// Defines the Driver
public WebDriver driver = new FirefoxDriver();
// Overrides the default driver
#Override
public WebDriver getDefaultDriver() {
return driver;
}
#Test
public void go_to_google()
{
goTo("http://www.google.com");
}
}

Resources