Mixing web and non-web cucumber/selenium tests avoiding web driver creation - java

I've inherited a Java/Cucumber test application and it mixes web/Selenium based tests and REST based tests.
When any test is ran, Cucumber is instantiating every class, e.g. my step and page factory classes. The problem is when running REST based tests the Chrome driver is started too and Chrome opens.
As below in the (cut-down code), the call to initElements() is the cause but I don't really understand if/when it's needed and how to remove/lazily load or somehow remove or move this call.
public class XXXPages {
#FindBy(xpath = "//input[#id='user_name']")
private WebElement usernameField;
private WebDriverFactory webDriverFactory;
public XXXPages(WebDriverFactory webDriverFactory) {
this.webDriverFactory = webDriverFactory;
//PROBLEM CALL
PageFactory.initElements(webDriverFactory.getDriver(), this);
}
public XXXPages searchName() {
WebDriverWait wait = new WebDriverWait(webDriverFactory.getDriver(), Duration.ofSeconds(3));
wait.until(ExpectedConditions.elementToBeClickable(searchNamefield));
searchNamefield.click();
return this;
}
}
public class WebDriverFactory implements Closeable {
private RemoteWebDriver webDriver;
final String driverPath = System.getProperty("webdriver.chrome.driver", "src/test/resources/drivers/linux/chromedriver");
private void createDriver(){
System.setProperty("webdriver.chrome.driver", driverPath);
final ChromeOptions chromeOptions = new ChromeOptions();
this.webDriver = new ChromeDriver(chromeOptions);
}
public RemoteWebDriver getDriver() {
if (this.webDriver == null){
createDriver();
return webDriver;
}
return webDriver;
}
#Override
public void close() {
webDriver.quit();
}
}

You need to design a wrapper for your driver in lazy initialization manner. Implement a class that would implement WebDriver interface and initialize the driver when the first call to the method happens. Like:
public class LazyWebDriver implements WebDriver, Disposable {
private WebDriver delegate = null;
private WebDriver getDelegate() {
if (delegate == null) {
String driverPath = System.getProperty("webdriver.chrome.driver", "src/test/resources/drivers/linux/chromedriver");
System.setProperty("webdriver.chrome.driver", driverPath);
ChromeOptions chromeOptions = new ChromeOptions();
delegate = new new ChromeDriver(chromeOptions);
}
return delegate;
}
#Override
public void get(String url) {
getDelegate().get(url);
}
// Override other methods here..
}
It might be useful for you to read this post where such approach is described in complete example.
Using the above approach would make cucumber create object for LazyWebDriver which won't cause real driver creation. The driver will be created only if you will execute the tests that use it.

Related

Null pointer exception in selenium cucumber JUnit Framework

Can someone tell the issue in my code for null pointer exception?
Error Message in console
=>test.pages.HomePage#31e75d13<=
[31mFailed scenarios:[0m
[31mE2E_Test.feature:3 [0m# Scenario: Test scenario
1 Scenarios ([31m1 failed[0m)
10 Steps ([31m1 failed[0m, [36m8 skipped[0m, [32m1 passed[0m)
0m12.461s
java.lang.NullPointerException
at org.openqa.selenium.support.pagefactory.DefaultElementLocator.findElement(DefaultElementLocator.java:69)
at org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler.invoke(LocatingElementHandler.java:38)
at com.sun.proxy.$Proxy17.sendKeys(Unknown Source)
at test.pages.HomePage.enterSearchText(HomePage.java:31)
at stepDefinitions.Steps.he_search_for(Steps.java:49)
at ✽.When he search for "test"(E2E_Test.feature:5)
Although I am getting the driver object and its not coming as Null as well but still getting a null pointer exception.
I am trying to run the selenium webdriver code to automate some test case. Here i am trying to open google.com and wants to enter some text in search box but after opening the google.com, when the execution reaches searchtextbox.sendkeys("test"), it gives null pointer exception. I tried debugging it to see if the homepage class object is coming as null or not but its showing the value and not null.
This is the test base class that i am using to initiate the google site and maximize the code
public class TestBase {
public static WebDriver driver;
public static Properties prop;
public static EventFiringWebDriver e_driver;
public static WebEventListener eventListener;
public TestBase() {
try {
prop = new Properties();
FileInputStream ip = new FileInputStream(System.getProperty("user.dir") + "/src/main/java/test" +
"/config/config.properties");
prop.load(ip);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// This method is used to initiatize the site url
public static void initialization(String url) {
String browserName = prop.getProperty("browser");
if (browserName.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\test\\Downloads\\driver\\chromedriver.exe");
driver = new ChromeDriver();
}
e_driver = new EventFiringWebDriver(driver);
// Now create object of EventListerHandler to register it with EventFiringWebDriver
eventListener = new WebEventListener();
e_driver.register(eventListener);
driver = e_driver;
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.manage().timeouts().pageLoadTimeout(TestUtil.PAGE_LOAD_TIMEOUT, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(TestUtil.IMPLICIT_WAIT, TimeUnit.SECONDS);
if (url == "google") {
driver.get(prop.getProperty("url"));
}
}
}
// Steps Definition file (Steps.java): This is the step defintion file // there is a function he_search_for called where the exception occurs
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
HomePage homePage;
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
#When("^he search for \"([^\"]*)\"$")
public void he_search_for(String arg1) throws InterruptedException {
System.out.print("=>" + homePage + "<=");
homePage.enterSearchText();
}
}
// HomePage Class is used to define all the page elements here in this class, i used enterSearchText function to enter the text in a search box.
public class HomePage extends TestBase {
#FindBy(name = "q")
WebElement searchTextBox;
WebDriver driver;
// Initializing the Page Objects:
public HomePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void enterSearchText() {
searchTextBox.sendKeys("Test");
}
}
Problem lies within your code design pattern between Classes Steps & TestBase. Please note
First, Class Steps is extending TestBase which already has WebDriver variable declared & initialized in it. So you do not need to again define WebDriver instance with in Steps. So please remove "WebDriver driver;" from below peace of code.
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
Second, Please do not declare WebDriver as static variable. Kindly declare it as Non-static as Keeping static may create problem during parallel execution as well.
public class TestBase {
public WebDriver driver;
Making WebDriver instance as non-static and having it as Thread safe
TestBase.java
public class TestBase {
public WebDriver driver;
public static Properties prop;
// This method is used to initiatize the site url
public synchronized void initialization(String url) {
String browserName = prop.getProperty("browser");
if (browserName.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\test\\Downloads\\driver\\chromedriver.exe");
driver = new ChromeDriver();
DriverManager.setWebDriver(driver);
}
}
}
DriverManager.java
import org.openqa.selenium.WebDriver;
public class DriverManager {
public static ThreadLocal<WebDriver> dr = new ThreadLocal<WebDriver>();
public static WebDriver getDriver() {
return dr.get();
}
public static void setWebDriver(WebDriver driver) {
dr.set(driver);
}
}
The problem is here
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
HomePage homePage;
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
#When("^he search for \"([^\"]*)\"$")
public void he_search_for(String arg1) throws InterruptedException {
System.out.print("=>" + homePage + "<=");
homePage.enterSearchText();
}
}
The WebDriver driver is null. You initialized WebDriver in initialization("google") method but you don't assign the value of created WebDriver to your driver
Additional line of code might help you.
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
this.driver = TestBase.driver; //asign initialized WebDriver to this instance variable
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
You can also remove the local WebDriver driver variable. Since TestBase contains static WebDriver, you can just use it directly since you use inheritance.
However, I highly suggest reading about WebDriverFactory or any similar term, like WebDriverManager. Anything to handle WebDriver instantiation without creating a static WebDriver. It will cause a lot of issues in the future with parallel execution.

Start Webdriver separately in Selenium Cucumber-jvm

I'm using cucumber and selenium.
I have three .feature files:
1)auth.feature
2)registration.feature
3)userInformation.feature
I have individual step definitons classes for them
1)authSteps.class
2)registrationSteps.class
3)userInformationSteps.class
IN each class I create webdriver like this
WebDriver driver = new WebDriver(ChromeDriver);
When I run the tests,all driver starts together,even if I have tagged test case and only run 1 case,the webdrivers are started too. #before doesn't work.
I want only to run the feature's webdriver. All if I test all features, I want to run the webdrivers in linear ways.
1.Create one AbstractHook class in TestScript package
public class AbstractHook {
protected static WebDriver driver;
protected WebDriver getdriver() {
if (driver == null) {
System.setProperty("webdriver.gecko.driver","geckodriver.exe");
driver = new FirefoxDriver();
} else if (driver == null) {
System.setProperty("webdriver.chrome.driver",
"chromedriver.exe");
driver = new ChromeDriver();
}
return driver;
}
2.Hook class
public class Hook extends AbstractHook {
AbstractHook df = new AbstractHook();
#Before
public void createdriver() {
df.getdriver();
driver.get("some url");// no need
}
}
3.TestScript code
public class TestScript01 extends AbstractHook {
WebDriver driver = getdriver();
#Given("^I want to open the gmail url on firefox$")
public void i_want_to_open_the_Gmail_url_on_firefox() throws Throwable {
driver.get("give some url");
}
}
4.Runner class
#RunWith(Cucumber.class)
#CucumberOptions(features = "Feature", monochrome = true, glue = { "com.scripts" }, plugin = { "pretty","html:target/cucumber" },
tags = {"#sa,#sanity" },
// dryRun = false)
public class RunnerClass {
}
Try in this way, The driver will be initialized once and used in all the TestScript classes .

Java - Fluentlenium how to run TestNG with threading for 1 method

I have a basic test using TestNG. When I run the test using invocationcount = 2, threadpoolsize = 2 (just for testing), I can see in intellij that the tests is running currently but only one browser open.
Heres' my code:
public class GoogleTesting extends FluentTestNg {
// Defines the Driver
public WebDriver driver = new ChromeDriver();
#Override
public WebDriver newWebDriver() {
return driver;
}
#Test(invocationCount = 2, threadPoolSize = 2)
public void GoogleTest(){
goTo("http://google.com");
System.out.println(getCookies());
}
}
Anyone know how to fix this?
Here you have one webdriver instance and calling in two threads. You can try with thread local WebDriver as given below.
public class GoogleTesting extends FluentTestNg {
// Defines the Driver
private static ThreadLocal<WebDriver> WebDriverTL = new ThreadLocal<WebDriver>();
public void setWebdriver(Webdriver driver){
WebDriverTL.set(driver);
}
#Override
public WebDriver newWebDriver() {
return WebDriverTL.get ();
}
#beforeMethod
public void launch browser(){
WebDriver driver = new ChromeDriver();
setWebdriver(driver);
}
#Test(invocationCount = 2, threadPoolSize = 2)
public void GoogleTest(){
goTo("http://google.com");
System.out.println(getCookies());
}
}

Using Selenium with TestNg, TestListenerAdapter is getting tests mixed, the driver seems to be getting shared between test classes

I am running parallel tests using selenium (selenium-server-standalone-2.47.1.jar) grid with TestNg, kicked off by ant, and using a TestListenerAdapter. Screenshots are taken in the listener's 'onTestFailure' method. The problem is that the listener seems to get crossed up about which driver it should be using, and sometimes takes a screenshot of the wrong browser window, or fails altogether if the driver that it thinks it should be using has already quit.
When the tests start, TestNg's #BeforeTest and the TestListenerAdapter's 'onTestStart' methods are running on the same thread, but when the test fails, the TestListenerAdapter's 'onTestFailure' method appears to be running on a separate thread. It seems like the threads are getting crossed/shared somehow, but I can't figure out why.
Here is some skeleton code, any assistance greatly appreciated.
Base Test Class:
public class baseClassTests{
protected AutomationUtils au;
protected DriverUtils du;
#BeforeTest(alwaysRun = true)
#Parameters({ "selenium.OS", "selenium.browser" })
public void beforeTest(String OS, String browser) {
//these call simple private methods to know where to set up the driver
String port = getPort(OS, browser);
String host = getHost(OS);
//make a driver utility object here, this makes a driver
du = new DriverUtils(browser, host, port);
//pass this driver utility object to another class of utilities
//this 'AutomationUtils' class gets a RemoteWebDriver ('driver') by calling driver=du.getDriver();
//the 'AutomationUtils' class is then the one that does all of the 'driver.findBy...' etc
au = new AutomationUtils(du);
}
#BeforeMethod(alwaysRun = true)
public void beforeMethod(Method m, ITestResult tr) {
du.deleteCookies();
testNgTestName = m.getName();
print("Method: "+testNgTestName + " Thread: "+Thread.currentThread().hashCode());
//set the attribute of the ITestResult object so we can use the same object in the listener
tr.setAttribute("du", du);
tr.setAttribute("au", au);
}
}
Listener class
public class AmSimpleTestListener extends TestListenerAdapter {
private DriverUtils driveU;
private AutomationUtils AutoU;
private RemoteWebDriver driver;
private RemoteWebDriver augmentedDriver;
private String methodName;
private String browser;
private String browserVersion;
String testClass;
#Override
public void onTestStart(ITestResult tr) {
//pick up the correct driver utility object from the test class/method we are in
driveU = (DriverUtils) tr.getAttribute("du");
AutoU = (AutomationUtils) tr.getAttribute("au");
driver = du.getDriver();
augmentedDriver = (RemoteWebDriver) new Augmenter().augment(driver);
methodName = tr.getName();
testClass=tr.getTestClass(); //sort of, I actually parse it up a bit
browser = driveU.getBrowser();
browserVersion = driveU.getBrowserVersion();
print("Method: "+methodName + " Thread: "+Thread.currentThread().hashCode());
}
#Override
public void onTestFailure(ITestResult tr) {
print("Method: "+tr.getName() + " Thread: "+Thread.currentThread().hashCode());
try{
writeScreenshotFile();
}
catch (Exception e){
Out.error("Unable to take screen shot");
e.printStackTrace();
}
}
private String writeScreenshotFile() {
if (driver != null && driver.getSessionId() != null) {
File scrShot = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
File localPathToScreenShot = new File("/path/to/base/directory/"+testClass+"/"+methodName+".png");
try {
FileUtils.copyFile(scrShot, localPathToScreenShot);
} catch (Exception e) {
Out.error("Couldn't write screenshot to file");
}
return localPathToScreenShot.getAbsolutePath();
}
return "Could not get path.";
}
}
DriverUtils class makes/supplies the driver
public class DriverUtils {
private RemoteWebDriver driver;
private int timeout;
private String browserVersion;
private String browser
private DesiredCapabilities caps;
public DriverUtils(String browser, String host, String port) {
String hostUrl = "http://" + host + ":" + port + "/wd/hub";
this.browser=browser;
//do some stuff here to set capabilties
driver = new RemoteWebDriver(new URL(hostUrl), caps);
browserVersion = driver.getCapabilities().getVersion();
}
public RemoteWebDriver getDriver() {
return driver;
}
public AmBrowser getBrowser() {
return browser;
}
public String getBrowserVersion() {
return browserVersion;
}
public void quitDriver() {
driver.quit();
}
public void deleteCookies(){
driver.manage().deleteAllCookies();
}
}
public class AutomationUtils extends BaseClassUtils {
public AutomationUtils(DriverUtils driverUtils) {
//pass it up to the base class utils (this is different than base class tests, above)
//do this so the driver can be accessed by other utility classes as well
super(driverUtils);
}
//All sorts of methods here to find elements, login, blah blah everything that is done with a driver object
}
public class BaseClassUtils { //this is a different class than BaseClassTests
//make the driver a protected object so all utility classes can access as nec.
protected final RemoteWebDriver driver;
public BaseClassUtils(DriverUtils driverUtils) {
driver = driverUtils.getDriver();
}
}
Tests are run via ant.
<suite name="Dev2 for debugging" parallel="tests" thread-count="10">-- tests here </suite>
After tinkering this for a while, I came to the conclusion that there were two things that seemed to help tremendously.
eliminate the listener, and take all screenshots in the #AfterMethod
Move the #Before/After Method/Test methods into the child classes, but simply call methods in the parent to do all the work.
Another thing I noticed is that for #2, TestNG is supposed to run the parent #Before methods then the child #Before methods; and then at the end run the child '#After' methods and then the parent #After methods.
I ran a series of simple tests, I found that all before/after methods were not being run, so for the few cases where I was using #Before and #After methods in both parent and child, I consolidated.
Things seem to run much better now, the driver does not get confused, and screenshots are being attached to the correct browser/test.
Try using a ThreadLocal for RemoteWebDriver so it can handle parallel runs:
public class DriverUtils {
private static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<~>();
private int timeout;
private String browserVersion;
private String browser
private DesiredCapabilities caps;
public DriverUtils(String browser, String host, String port) {
String hostUrl = "http://" + host + ":" + port + "/wd/hub";
this.browser=browser;
//do some stuff here to set capabilties
driver.set(new RemoteWebDriver(new URL(hostUrl), caps));
browserVersion = getDriver().getCapabilities().getVersion();
}
public RemoteWebDriver getDriver() {
return driver.get();
}
public AmBrowser getBrowser() {
return browser;
}
public String getBrowserVersion() {
return browserVersion;
}
public void quitDriver() {
getDriver().quit();
}
public void deleteCookies(){
getDriver().manage().deleteAllCookies();
}
}

Using Common Selenium WebDriver instance

I want use a common WebDriver instance across all my TestNG tests by extending my test class to use a base class as shown below but it doesn't seem to work :
public class Browser {
private static WebDriver driver = new FirefoxDriver();
public static WebDriver getDriver()
{
return driver;
}
public static void open(String url)
{
driver.get(url);
}
public static void close()
{
driver.close();
}
}
I want to use the WebDriver in my test class as shown below, but I get the error message :
The method getDriver() is undefined for the type GoogleTest:
public class GoogleTest extends Browser
{
#Test
public void GoogleSearch() {
WebElement query = getDriver().findElement(By.name("q"));
// Enter something to search for
query.sendKeys("Selenium");
// Now submit the form
query.submit();
// Google's search is rendered dynamically with JavaScript.
// Wait for the page to load, timeout after 5 seconds
WebDriverWait wait = new WebDriverWait(getDriver(), 30);
// wait.Until((d) => { return d.Title.StartsWith("selenium"); });
//Check that the Title is what we are expecting
assertEquals("selenium - Google Search", getDriver().getTitle().toString());
}
}
The problem is that your getDriver method is static.
Solution #1: Make method non-static (this will either need to make the driver variable non-static as well, or use return Browser.getDriver(); )
public WebDriver getDriver() {
return driver;
}
Or, call the getDriver method by using Browser.getDriver
WebElement query = Browser.getDriver().findElement(By.name("q"));
You need to start your driver, one of many solution is to try #Before to add, Junit will autorun it for you.
public class Browser {
private WebDriver driver;
#Before
public void runDriver()
{
driver = new FirefoxDriver();
}
public WebDriver getDriver()
{
return driver;
}
public void open(String url)
{
driver.get(url);
}
public void close()
{
driver.close();
}
}

Categories