How to handle browser invocation in parallel tests using TestNG - selenium-webdriver

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.

Related

Test Case breaking when running the application as a TestNG test

I wrote a java application to use the selenium webdriver to automate few web application tasks. They all worked fine when run as a Java application.
To use the TestNG reporting feature, I ran the application as a TestNG test instead of JAVA application. The same test which was working as JAVA application is failing when I run as testNG.
However, I'm guessing I've setup the TestNG properly since the first testcase which is used to login to the webapp is passing (webLogin method in the code below)
I'm using chromeDriver. I tried removing the main method to run it as testNG application. But it did not work. I made sure that the element path locators I'm using are still valid while using testNG.
package myPackage;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.*;
import com.google.common.base.Function;
public class checkNavigation {
public static WebDriver driver;
public static WebDriverWait wait;
public static void main(String args[]) throws InterruptedException{
Configure();
wait = new WebDriverWait(driver, 30);
webLogin();
addClient();
addGoal();
addInsurance();
validateInputs();
endSession();
}
#BeforeTest
public static void Configure() {
System.setProperty("webdriver.chrome.driver", "/Users/divyakapa/Desktop/automation/chromedriver");
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.manage().timeouts().pageLoadTimeout(40, TimeUnit.SECONDS);
driver.get("https://example.com");
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
#Test
public static void webLogin() {
getElement(By.xpath("//*[#id=\"id\"]")).sendKeys("admin");
getElement(By.xpath("//*[#id=\"pw\"]")).sendKeys("password");
getElement(By.xpath("//*[#id=\"ember383\"]/div/div/form/button/span")).click();
}
#Test
public static void addClient() {
getElement(By.xpath("//*[#id=\"ember744\"]/button/div")).click();
getElement(By.xpath("//*[#id=\"ember744\"]/div/button[1]/div[2]/div")).click();
getElement(By.xpath("//*[#id=\"newInputFirst\"]")).sendKeys("firstName");
getElement(By.xpath("//*[#id=\"newInputLast\"]")).sendKeys("lastName");
getElement(By.xpath("//*[#id=\"newPersonInputBirthday\"]")).sendKeys("1991");
Select location = new Select(driver.findElement(By.xpath("//*[#id=\"newUserInputProvince\"]")));
location.selectByVisibleText("Place1");
Select isRetired = new Select(driver.findElement(By.xpath("//*[#id=\"alreadyRetiredDropdown\"]")));
isRetired.selectByVisibleText("No");
Select age = new Select(driver.findElement(By.xpath("//*[#id=\"newRetirementAge\"]")));
age.selectByVisibleText("60");
getElement(By.xpath("//*[#id=\"data-entry-modal\"]/div[2]/div/div[1]/div[2]/button[2]")).click();
}
#Test
public static void addGoal() {
getElement(By.xpath("//*[#id=\"ember2328\"]/button/div")).click();
getElement(By.xpath("//*[#id=\"ember2328\"]/div/div[1]/div[2]/button[3]")).click();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[#id=\"ember2464\"]/ul/li[1]/div/div/div[2]/div/span"))).click();
getElement(By.xpath("//*[#id=\"basicExpenseInputAmount\"]")).clear();
getElement(By.xpath("//*[#id=\"basicExpenseInputAmount\"]")).sendKeys("90000");
getElement(By.xpath("//*[#id=\"ember2563\"]/div/div[2]/div[2]/button[2]")).click();
// Add income
getElement(By.xpath("//*[#class=\"add-button \"]")).click();
getElement(By.xpath("//*[#data-test-model-type=\"income\"]")).click();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[#class=\"list-group\"]/li[1]"))).click();
getElement(By.xpath("//*[#id=\"employmentInputName\"]")).clear();
getElement(By.xpath("//*[#id=\"employmentInputName\"]")).sendKeys("Company");
getElement(By.xpath("//*[#id=\"employmentInputSalary\"]")).sendKeys("95000");
getElement(By.xpath("//*[#id=\"employmentInputBonus\"]")).sendKeys("5000");
getElement(By.xpath("//*[#id=\"employmentInputBenefitsInKind\"]")).sendKeys("1000");
getElement(By.xpath("//*[#aria-label=\"Save\"]")).click();
}
#Test
public static void addInsurance() {
getElement(By.xpath("//*[#class=\"add-button \"]")).click();
getElement(By.xpath("//*[#data-test-model-type=\"protection\"]")).click();
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[#class=\"list-group\"]/li[1]"))).click();
getElement(By.xpath("//*[#id=\"termLifeName\"]")).clear();
getElement(By.xpath("//*[#id=\"termLifeName\"]")).sendKeys("BlueCrossBlueShield");
getElement(By.xpath("//*[#id=\"ukTermProtectionSalaryMultiplier\"]")).clear();
getElement(By.xpath("//*[#id=\"ukTermProtectionSalaryMultiplier\"]")).sendKeys("5");
Select empId = new Select(driver.findElement(By.xpath("//*[#id=\"termLifeInsuranceEmploymentId\"]")));
empId.selectByVisibleText("Company");
getElement(By.xpath("//*[#aria-label=\"Save\"]")).click();
}
#Test
public static void validateInputs() {
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
if(!(driver.findElements(By.xpath("//*[#data-test-accordion-header=\"goals\"]")).size() > 0)) throw new NullPointerException("Income Failed to ADD");
if(!(driver.findElements(By.xpath("//*[#data-test-accordion-header=\"income\"]")).size() > 0)) throw new NullPointerException("Income Failed to ADD");
if(!(driver.findElements(By.xpath("//*[#data-test-accordion-header=\"protection\"]")).size() > 0)) throw new NullPointerException("Income Failed to ADD");
}
public static WebElement getElement(final By locator) {
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver).ignoring(NoSuchElementException.class);
WebElement element = wait.until(new Function<WebDriver, WebElement>() {
#Override
public WebElement apply(WebDriver arg0) {
return arg0.findElement(locator);
}
});
return element;
}
#AfterTest
public static void endSession() {
driver.close();
driver.quit();
}
}
Running the above code, the get the following error :
Default suite
Total tests run: 5, Failures: 4, Skips: 0
I also see that it takes a lot of time (about 10 seconds) before the page is logged in even though that test passes. This does not happen when I run the code as a Java application
Can you show which tests are actually failing? if you are looking for order in testng test execution, it doesn't come by default , so if you have to run test2 after test1 and test3 after test2 etc then you have to use priority(lower the number higher the priority) for ex,
#Test(priority=1)
public void Test1() {
}
#Test(priority=2)
public void Test2() {
}
#Test(priority=3)
public void Test3() {
}
hope this helps
No, testng never guarantees ordering by default
TestNG relies on reflection. The Java Reflection APIs does not guarantee the method order when we use it to introspect a class to find out what are the test methods that are available in it. So the order of independent methods (Methods that dont have either soft or hard dependency) execution is never guaranteed.
Soft dependency - This is usually achieved in TestNG by using the priority attribute for the #Test annotation. Its called a soft dependency because TestNG will continue to execute all the methods even though a previous method with a higher priority failed.
Hard dependency - This is usually achieved in TestNG by using either dependsOnMethods (or) dependsOnGroups attribute for the #Test annotation. It's called a hard dependency because TestNG will continue to execute a downstream method if and only if an upstream method ran successfully.
By default testng executes methods in alphabetical order of the method names. Typically you would not be using main method for testng. Annotations along with priority are used to set the sequence of executions
Testng framework will run the test methods in alphabetical order. I could see your test methods are dependent and it should in the order. You can set the priorities for your test methods the way you want it to run.
You can refer the below link to set the priority.
TestNG priority set

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.

Calling data from one class to another class - framework for JUnit

Please forgive me as I am still very new to the world of test automation. I have started out by using Selenium WebDriver with JUnit4, predominately on windows OS, although I have modified my scripts and ran them on Mac.
I want to be able to create a set of classes containing set data such as usernames, passwords, default url . Perhaps even calling them from an excel file, but for now Im happy to store the data in classes and then pass that data into other test classes. Im guessing this would be a framework of some sort.
Currently I am writing classes that all begin with something like:
public class ExampleSQATest{
public static Chromedriver chrome;
#BeforeClass
public static void launchBrowser(){
System.setProperty("webdriver.chrome.driver", "chromedriver/chromedriver.exe");
chrome = new ChromeDrievr();
}
#Test
public void aLogin(){
chrome.manage().window().maximize();
chrome.navigate().to("http://mydummywebsite.com");
new WebDriverWait(chrome, 10).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector
("input#UserName")));
WebElement username = chrome.findElementByCssSelector("input#UserName");
username.sendKeys("username");
WebElement password = chrome.findElementByCssSelector("input#Password");
password.sendKeys("password");
WebElement submit = chrome.findElementByCssSelector("input[type='submit']");
submit.click();
}
}
I will then proceed to write further test methods which requires entering data, but I'd like to be able to call this data from somewhere else that is already predefined.
Can anyone provide any suitable suggestions to investigate so I can learn. Something that is a guide or tutorial. Nothing too advanced, just something that helps me get started by advising me how to set a class of methods to be called by other classes and how it all links together as a framework.
Many thanks in advance.
One way to do this
public abstract class TestBase
{
private readonly INavigationManager navMgr;
private readonly IWindowNavigator windowNav;
private readonly ILoginManager loginMgr;
// All your stuff that is common for all the tests
protected TestBase()
{
this.navMgr = WebDriverManager.Get<INavigationManager>();
this.windowNav = WebDriverManager.Get<IWindowNavigator>();
this.loginMgr = WebDriverManager.Get<ILoginManager>();
}}
[TestFixture]
internal class QueriesTest : TestBase
{
private QueryTests queryTests;
[SetUp]
public void Setup()
{
this.queryTests = WebDriverManager.Get<QueryTests>();
// all the stuff you run specific before tests in this test class.
}
}
Assuming you have created test classes in webdriver-junit4, Use following two classes to call your test classes (Note-Import junit annotations)
1)Create test suite class as -
#RunWith(Suite.class)
#Suite.SuiteClasses({
YourTestClass1.class,
YourTestClass2.class,[you can add more tests you have created...]
})
public class TestSuiteJU {
}
2)Create class to call suite created above as-
public class TestExecution {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(TestSuiteJU .class);
}
}

How to run all cucumber functional automated test cases in single browser

I am new to cucumber testing as well as selenium testing.Please help me to run all cucumber test cases in single browser.As for now i am creating new WebDriver object in each cucumber step_def for feature file.
The solution is, Using / passing the same Web Driver object across your step_def. From your Question i assume, you have multiple Step Def files, If the stories are small and related put all of them in a single step_def file and have a single Web driver object. If it is not the case, invoke every step_def with a predefined Driver object that is globally declared in the configuration loader.
For using one browser to run all test cases use singleton design pattern i.e make class with private constructor and define class instance variable with a private access specifier.Create a method in that class and check that class is null or not and if it is null than create a new instance of class and return that instance to calling method.for example i am posting my code.
class OpenBrowserHelp {
private WebDriver driver;
private static OpenBrowserHelp browserHelp;
private OpenBrowserHelp() {
this.driver = new FirefoxDriver()
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.manage().window().maximize();
}
public static OpenBrowserHelp getOpenBrowserHelp() {
if (null == browserHelp) {
browserHelp = new OpenBrowserHelp();
}
return browserHelp;
}
WebDriver getDriver() {
return driver
}
void setDriver(WebDriver driver) {
this.driver = driver
}
public void printSingleton() {
System.out.println("Inside print Singleton");
}
Now, where ever you need to create browser instance than use
WebDriver driver = OpenBrowserHelp.getOpenBrowserHelp().getDriver();

Resources