I have a the following selenium test suite inheriting from the same base class, how to I have the tests use the same web driver instance when i run the entire test suite?. I also want to run each tests in isolation aswell. I believe this will cut down the time it takes to run the suite considerably.
This test is run from maven that in turn runs each test class.
#RunWith(Suite.class)
#SuiteClasses({
AdminPortalTestSuite.class,
DevPortalTestSuite.class,
CommonTestSuite.class
})
public class SeleniumTestSuite {
}
Baseclass all tests inherit from
#BeforeClass
public static void setUp() throws Exception {
if (null == baseUrl || !baseUrl.startsWith("https://")) {
baseUrl = "https://localhost:8443";
}
if (null == browser || browser.startsWith("${")) {
browser = "firefox";
}
//retrieve properties including locale.
retrieveProperties();
Thread.sleep(4000);
setUpDriver();
}
#After
public void tearDownAfterTest() {
openUrl(LIST_PARTNERS);
adminPortalLogout();
openUrl(DASHBOARD);
developerPortalLogout();
driver.manage().deleteAllCookies();
}
#AfterClass
public static void tearDown() throws Exception {
BaseFunctionalTestCase.driver.quit();
}
test example
public class APApplicationFunctionalTestCase extends BaseFunctionalTestCase {
/**
* Test validation when creating a new application.
*/
#Test
public void testApplicationValidation() {
Assume.assumeTrue(preHtml5);
final String partnerName = randomize("partner");
//create partner
createPartnerThroughAP(partnerName);
adminPortalLogin();
openUrl(ADD_APPLICATION + partnerName);
waitForId("applicationView.applicationName");
findById("submit-button").click();
waitForId("submit-button");
//check validation
assertTrue("Failed to validate application name",
isTextPresent(resolveAPMessage("partner", "application.messages",
"NotEmpty.applicationEditView.applicationView.applicationName")));
assertTrue("Failed to validate application username",
isTextPresent(resolveAPMessage("partner", "application.messages",
"NotEmpty.applicationEditView.applicationView.applicationUserName")));
assertTrue("Failed to validate application password",
isTextPresent(resolveAPMessage("partner", "application.messages",
"Password.applicationEditView.applicationView.applicationPassword")));
assertTrue("Failed to validate application password confirmation",
isTextPresent(resolveAPMessage("partner", "application.messages",
"Length.applicationEditView.applicationPasswordConfirmation")));
}
This is how I did it. In SeleniumTestSuite, I added a static WebDriver and instantiate it in a setUp() method annotated with #BeforeClass. Then, in the Base class that all of my selenium tests inherit from, I added a getDriver() method, that will try to get the static driver from SeleniumTestSuite. If that driver is null, then a new one gets instantiated and returned. Thus, when the selenium test classes are running via the suite, they will use the driver from SeleniumTestSuite, and when they are running individually, they will use their own driver.
SeleniumTestSuite:
#RunWith(Suite.class)
#SuiteClasses({
AbcSeleniumTest.class,
XyzSeleniumTest.class
})
public class SeleniumTestSuite {
private static WebDriver driver;
#BeforeClass
public static void setUp() {
driver = new FirefoxDriver();
}
//driver getter/setter
}
BaseSeleniumTest:
public abstract class BaseSeleniumTest {
public WebDriver getDriver() {
WebDriver driver = SeleniumTestSuite.getDriver();
if(driver != null) {
return driver;
}
return new FirefoxDriver();
}
}
AbcSeleniumTest:
public class AbcSeleniumTest extends BaseSeleniumTest {
#Test
public void testAbc() {
WebDriver driver = getDriver();
// test stuff
}
}
I'm not great with JUnit... looks like you're trying the solution suggested here:
Before and After Suite execution hook in jUnit 4.x
which would suggest you should move your #BeforeClass into your SeleniumTestSuite class.
Related
While learning TestNG on Udemy, I come across a code that I am unable to understand. The instructor has created a class named "TestBase" where he defined #BeforeMethod/#aftermethod.Later he created another class named "LoginTest" where he wrote the actual test with #test. He extended TestBase class in loginTest to get variable initiated in TestBase class. When he ran loginTest then #BeforeMethod/#aftermethod also ran with this. How did these two methods ran along with #test when these methods are in different classes. here are both codes:
public class TestBase {
public static String getURL() {
String URL = null;
switch (GetProperties.getPropertyValueByKey("env")) {
case "qa":
URL = GetProperties.getPropertyValueByKey("qaUrl");
break;
case "dev":
URL = GetProperties.getPropertyValueByKey("devUrl");
break;
case "uat":
URL = GetProperties.getPropertyValueByKey("uatUrl");
break;
case "prod":
URL = GetProperties.getPropertyValueByKey("prodUrl");
break;
default:
LogFactory.info("No env has been set in Properties file");
}
return URL;
}
#BeforeMethod
public void setup() {
//ToDo: Pass browser value from config.properties
WebDriver driver = BrowserFactory.create(GetProperties.getPropertyValueByKey("browser"));
DriverFactory.setDriver(driver);
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.get(getURL());
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(Constants.PAGE_LOAD_TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(Constants.IMPLICIT_WAIT));
}
#AfterMethod
public void tearDown() {
if (null != DriverFactory.getDriver()) {
try {
DriverFactory.getDriver().quit(); // quit WebDriver session gracefully
DriverFactory.removeDriver();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
public class LoginTest extends TestBase {
/**
* Below Login Test case has hardcoded data being passed from test method itself
**/
#Test(description = "Verify agent login with valid credentials")
public void loginWithValidCredentials() {
LoginPage loginPage = new LoginPage();
DashboardPage dashboardPage = new DashboardPage();
loginPage.loginWithValidUser("xyx#yopmail.com", "Hello1136");
try {
Thread.sleep(10000); // Added just for now will remove this in future and will implement proper selenium waits !
} catch (InterruptedException e) {
e.printStackTrace();
}
Assert.assertEquals(dashboardPage.getDashboardPageURL(), Constants.URL + "/dashboard/");
}
}
If you read this line :
public class LoginTest extends TestBase
this clearly tells that, LoginTest is a child class of TestBase .
so TestBase gets more precedence.
Now let's understand what is #BeforeMethod.
#BeforeMethod
The annotated method will be run before each test method.
so this is by default Testng architecture to run #BeforeMethod before each #Test in your test suite.
Your program execution should be in this order :-
#BeforeMethod
then
#Test
then
#AfterMethod
if you have more than one #Test, the order should be same.
You can refer here and the above reference has been taken from TestNG official docs.
So #beforeMethod and #AfterMethod will run before and after your actual Test, that is how the TESTNG lifecycle works.
You can find more details here
I recently switched to using ThreadLocal in my test suite, and while it has worked great for parallel tests, it has now broken non-parallel tests.
I have #BeforeClass/#BeforeMethod methods that handle creating a new instance of WebDriver based on the test class's driver instance policy. However, when they are creating a new instance of WebDriver it is tied to the thread of "Test Worker" which is only used in configuration methods, like #BeforeClass and #BeforeMethod. This doesn't happen when running with parallelization enabled. Here's a very simplified example:
public class DriverWrapper () {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public DriverWrapper() {}
public void newInstance(){
driver.set(new ChromeDriver());
}
public WebDriver getDriverInstance(){
return driver.get();
}
}
abstract public class AbstractTest {
DriverPolicy policy;
DriverWrapper driver;
public AbstractTest(DriverPolicy policy){
this.policy = policy;
}
#BeforeClass
public void setUpClass() {
if (policy == DriverPolicy.NEW_INSTANCE_PER_CLASS) {
driver = new DriverWrapper();
driver.newInstance();
}
}
#BeforeMethod
public void setUp() {
if (policy == DriverPolicy.NEW_INSTANCE_PER_METHOD) {
driver = new DriverWrapper();
driver.newInstance();
}
}
}
public class SomeTest {
public SomeTest() {
super(DriverPolicy.NEW_INSTANCE_PER_CLASS);
}
#Test
public void doSomeThings() {
SomePageFactoryClass somePageFactoryClass = new SomePageFactoryClass(driver.getDriverInstance());
// it does some pagefactory stuff
somePageFactoryClass.login();
// login() tries to do some WebDrivery stuff
// It's here that I hit the NPE because the thread ID that got assigned to the WebDriver in
// DriverWrapper is different from the thread ID in #Test, so it's just null.
}
}
My testng.xml that runs these tests only encounters this issue when parallel is disabled. As soon as I enable parallel, #BeforeClass and #BeforeMethod methods are running on the same thread as their class's #Test methods.
I've come up with a messy work around where I'm just creating the new instances of DriverWrapper in each individual #Test method, but it feels wasteful when I have the code already sitting in AbstractTest.
Is there something obvious I'm missing? Maybe I should just not use ThreadLocal for non-parallel tests?
Selenium with TestNG Script is not working? Script is not working in testng framework but in a modular framework, it is working fine ?? Here is the code snapshot.
//Script To Register Inside The Application
public class AppTest{
WebDriver driver;
#BeforeMethod
public void setup() {
System.setProperty("webdriver.chrome.driver","F:\\ResourceFiles\\chromedriver_win32\\chromedriver_win32\\chromedriver.exe");
WebDriver driver =new ChromeDriver();
driver.get("http://newtours.demoaut.com/mercurywelcome.php");
driver.manage().window().maximize();
// driver.manage().timeouts().implicitlyWait(8000, TimeUnit.SECONDS);
}
#Test
public void registerApp(){
//driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[2]/td/table/tbody/tr/td[2]/a")).click();
driver.findElement(By.linkText("REGISTER")).click();
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[2]/td[2]/input")).sendKeys("kartikey");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[3]/td[2]/input")).sendKeys("gautam");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[4]/td[2]/input")).sendKeys("7248006980");
driver.findElement(By.xpath("//*[#id=\"userName\"]")).sendKeys("kartikeygautam#gmail.com");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[7]/td[2]/input")).sendKeys("22 Dayal Bagh Colony");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[9]/td[2]/input")).sendKeys("Agra");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[10]/td[2]/input")).sendKeys("UttarPradesh");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[11]/td[2]/input")).sendKeys("282005");
Select fruits = new Select(driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[12]/td[2]/select")));
fruits.selectByVisibleText("INDIA ");
driver.findElement(By.xpath("//*[#id=\"email\"]")).sendKeys("kartikeygautam#gmail.com");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[15]/td[2]/input")).sendKeys("Mummyp#p#123");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[16]/td[2]/input")).sendKeys("Mummyp#p#123");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[16]/td[2]/input")).click();
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[5]/td/form/table/tbody/tr[18]/td/input")).click();
}
#Test
public void loginApp() {
driver.get("http://newtours.demoaut.com/mercurywelcome.php");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[2]/td[3]/form/table/tbody/tr[4]/td/table/tbody/tr[2]/td[2]/input")).sendKeys("kartikeygautam");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[2]/td[3]/form/table/tbody/tr[4]/td/table/tbody/tr[3]/td[2]/input")).sendKeys("Mummyp#p#123");
driver.findElement(By.xpath("/html/body/div/table/tbody/tr/td[2]/table/tbody/tr[4]/td/table/tbody/tr/td[2]/table/tbody/tr[2]/td[3]/form/table/tbody/tr[4]/td/table/tbody/tr[4]/td[2]/div/input")).click();
}
#AfterMethod
public void tearDown() {
driver.quit();
}
}
Your #BeforeMethod is not setting a value to the class instance of driver, it's generating a new driver object that only exists in the context of the #BeforeMethod. You need to tweak your #BeforeMethod to be:
#BeforeMethod
public void setup() {
System.setProperty("webdriver.chrome.driver","F:\\ResourceFiles\\chromedriver_win32\\chromedriver_win32\\chromedriver.exe");
driver = new ChromeDriver();
driver.get("http://newtours.demoaut.com/mercurywelcome.php");
driver.manage().window().maximize();
}
I have a JUnit Framework with the following annotations #before, #test, #After.
However when I run through my tests the #After Annotation is never initialised and therefore the browser doesn't close.
I've run my tests using JUnit and they all pass but the tear down step never works.
I decided to try see if any of the annotations worked, so I removed the #before and #test and the test still run and passed which suggests to me that they are not being used at all.
This is my Selenium set up:
public SeleniumSetup() {
}
#Before
public void prepareBrowserForSelenium() throws Exception {
// setup();
if(DriverSingleton.getDriver() == null)
{
setup();
}
else
{
driver = DriverSingleton.getDriver();
}
}
public void setup() throws Exception {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\antho\\Automation\\WebAutomation\\chromedriver.exe");
driver = new ChromeDriver();
driver.get("https://www.ultimateqa.com");
driver.manage().window().maximize();
DuringTest();
}
#Test
public void DuringTest() throws Exception{
System.out.println("test has started");
assertEquals(true, driver.getPageSource().contains("Learn Critical Automation and Dev Skills"));
System.out.println("Learn Critical Automation and Dev Skills copy has been verified");
driver.findElement(By.linkText("Automation Exercises")).click();
assertEquals(true, driver.getPageSource().contains("Automation Practice"));
System.out.println("Automation Practice copy has been verified");
driver.findElement(By.linkText("Big page with many elements")).click();
}
#After
public static void tearDown() throws Exception {
driver.close();
}
This is my DriverSingleton;
private static WebDriver driver;
public DriverSingleton () {
}
public static WebDriver getDriver() {
return driver;
}
public static void setDriver (WebDriver driver) {
DriverSingleton.driver = driver;
}
}
After my tests have run I expect the browser to close down.
I have added DuringTest(); to my #Before class, which I suspect is the only reason the #Test class is getting called, without that this the #Test doesn't work.
First you shall not invoke #Test inside your setup method by calling DuringTest(). JUnit framework would take care of Test Method by looking #Test annotations. So you do not need to call it explicitly.
Make both #Before & #After method as non static.
If you were running a test with this code you should have seen an exception with the following message being thrown by JUnit:
Method tearDown() should not be static
Method annotated with JUnit test annotations should always be non-static and located inside dedicated classes used exclusively for running tests. Use the following code as a base to further build your test class:
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class SeleniumTest {
#Before
public void prepareBrowserForSelenium() throws Exception {
System.out.println("Running method annotated with #Before");
}
#Test
public void DuringTest() throws Exception {
System.out.println("Running main test method");
}
#After
public static void tearDown() throws Exception {
System.out.println("Running method annotated with #After");
}
}
Console output:
Running method annotated with #Before
Running main test method
Running method annotated with #After
As you see everything work just fine. I would suggest reading more about JUnit testing in general and here is a good place to start:
https://github.com/junit-team/junit4/wiki/Getting-started
I am starting to use selenium webdriver with TestNG. I have created a multiple-test class which can run multiple tests, however, how do I call the other classes in each test block without copying the whole code over?
public WebDriver driver;
//Test 1
#Test(priority = 0) //Set Priority of Test - Priority of test always starts from Zero
public void one() {
System.out.println("This is Test Case 1");
}
//Test 2
#Test(priority = 1) // Test priority 1
public void Two(){
System.out.println("This is Test Case 2");
}
Would I need to create a function to call in each test block to run the other classes?
Use the setUp() method as described here to instantiate that class and keep it as an attribute.
The method setUp() will be invoked after the test class has been built
and before any test method is run.
import org.testng.annotations.*;
public class MyTest {
private MyService myService;
#BeforeClass
public void setUp() {
myService = new MyService();
}
#Test
public void testSomething() {
myService.doSomething();
}
}