How to pass selenium webdriver instance to another class - java

I browsed through the site but did not find the answer I am looking.
I have
Superbase class- here I just create object of a webdriver
Baseclass- In this class I extend Superbase class, invoke the driver, and open the URL.
Clicklink class- In this class, I again extend the Superbase Class but only to find a null pointer exception. I think I am getting the exception as the driver object is not initialized.
I am just a beginner, and have not tried the browserfactory and other options, as I want to start with simple flow.
Superclass
Public class Superclass
{
public webdriver Driver;
}
Baseclass
public class Baseclass extends Superclass
{
setting capabilities and launching the browser
}
ClickLink
public class Clicklink extends Superclass
{
here I want to click on a link
driver.findelement(by.xpath("xpath").click());
// after this statement I get a null pointer exception
}
Can you please guide me here? how can I achieve the same.
Thanks much!

SuperClass and BaseClass are very poor names. Do not use the language of coding to name your classes use the language of the problem. In this case, web site application testing, use LoginPage, CartPage, ProfilePage, etc. Use the Page Object Pattern.
I suggest you use the Factory Pattern to provide the instances of WebDriver for each test. Since all those fit the idea is a page, use class extension from a standard PageObject to provide this capability. When navigating, have the current page construct an instance of the new page and pass it the current webDriver connection instance. Then any manipulations you apply to that PageObject will be automatically applied to that webDriver instance and its associated browser instance. You should also use a PageFactory to provide instances of the pageObject.
public abstract class PageObject {
public WebDriver driver;
PageObject() {
// Page can initialise its self
this.driver = BrowserFactory.webDriver();
}
PageObject(final WebDriver webDriver) {
this.driver = webDriver;
}
}

This is lot of guesswork done from my side, but please make sure, that your Superclass actually sets the driver and returns it. You can actually make it in both methods:
public class Superclass
{
public WebDriver driver;
public Superclass(){
driver = new FirefoxDriver();
}
public WebDriver getdriver(){
if (driver == null){
driver = new FirefoxDriver();
return driver;
}else{
return driver;
}
}
}
And later in methods you call it by:
public class Clicklink extends Superclass
{
getdriver().findelement(by.xpath("xpath").click());
}

If you doesn't want pass driver instance to Page Objects constructor you could create some container class for driver and put it before test and remove it after run. For example:
class Driver {
public static ThreadLocal<IWebDriver> driverInstance = new ThreadLocal<IWebDriver>();
public static IWebDriver GetDriver() {
return driverInstance.Value;
}
public static void SetDriver(IWebDriver driver) {
driverInstance.Value = driver;
}
}
and make this container field ThreadLocal to avoid problems with parallel run.

I have taken a slightly different approach than most on this thread. When I start a test session, I pass the browser name as an argument (i.e. -Dbrowser=chrome) in order to be able to test my web application with different browsers. Then I used the "browser" system property to obtain the browser name when setup() is called by my test framework. In my case, I use JUnit annotations in order to JUnit to setup all needed dependencies prior to running any tests.
#BeforeClass
public static void setup() throws Exception {
// Set up other stuff
String browser = System.getProperty("browser");
try {
SessionDataProvider.driver = TestUtils.createDriver(browser);
} catch (Exception e) {
...
}
}
The createDriver(String) is a factory method that instantiates the correct driver.
public static WebDriver createDriver(String browserName) throws Exception {
WebDriver driver = null;
try {
switch(browserName) {
case "firefox":
// code to system props and instantiate the driver
break;
case "chrome":
// code to system props and instantiate the driver
break;
case "ibrowser":
// code to system props and instantiate the driver
break;
case "edge":
// code to system props and instantiate the driver
break;
case "safari":
// code to system props and instantiate the driver
break;
default:
throw new Exception("Unsupported browser: " + browserName);
}
return driver;
}
Then, when I execute a step definition, I simply obtain the driver from the data provider class:
#And("(I click)/Click on {string}")
public void click(String arg) {
// Parse String arg and create web element locator...
try {
By locator = ...;
WebElement element = new WebDriverWait(SessionDataProvider.driver, 2)
.until(ExpectedConditions.elementToBeClickable(locator));
element.click();
} catch (Exception e) {
// handle exception
}
}

I did use below code in utility class like below
public static WebDriver setup(WebDriver driver)
{
if(driver == null) {
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
return driver;
}else
return driver;
//System.out.println("in method "+ driver.getTitle() );
}

Related

Java automation selenium convert to Pom or factory

I need help with creating Java automation with Selenium Webdriver to Pom or Pomm factory.
I've read how to create pom without any success.
Please help.
I need help on how to create java automation in pom. strong textHow to conver it?
String baseUrl = "https:amazon.com/";
WebDriver driver;
NavigationPom navigationPom;
private final boolean useFirefoxbrowser = false;
#BeforeClass
public static void setupClass() {
WebDriverManager.chromedriver().setup();
WebDriverManager.firefoxdriver().setup();
}
#Before
public void setUp() {
if (useFirefoxbrowser == false) {
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.addArguments("--width=1240", "--height=720");
driver = new FirefoxDriver(firefoxOptions);
} else {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--window-size=1920,1080");
driver = new ChromeDriver(chromeOptions);
}
}
#Test
public void MacbookTest1() {
driver.get(baseUrl);
driver.manage().window().maximize();
driver.findElement(By.xpath("//input[#id='twotabsearchtextbox']")).click();
driver.findElement(By.xpath("//input[#id='twotabsearchtextbox']")).sendKeys("macbook");
driver.findElement(By.xpath("//input[#id='nav-search-submit-button']")).click();
driver.findElement(By.xpath("//input[#id='nav-search-submit-button']")).click();
driver.findElement(By.xpath("//li[#id='p_89/Lenovo']/span/a/div/label/i")).click();
//Checkboxes
boolean enabled = driver.findElement(By.xpath("//li[#id='p_89/Lenovo']/span/a/div/label/i")).isEnabled();
Start from creating a simple implementation:
How the Page class might look:
class Page {
private WebDriver driver;
public Page(WebDriver driver) {
this.driver.driver = driver;
}
//Define locators
//Keep elements as By on the top of the class
private By someElement1 = By.xpath("...");
private By someElement2 = By.xpath("...");
//Define page methods
// Examples:
// public void clickSomeElement()
// public void fillSomeInput()
public void doSomeAction() {
WebElement element = driver.findElement(someElement1);
// do something with the element
// optionaly wait for something after action
}
// Examples:
// public boolean isPageLoaded()
// public boolean isSomeElementDisplayed()
// public String getSomeElementText()
// public int getSomeElementsCount()
public Object getSomeData() {
WebElement element = driver.findElement(someElement2);
// do something with the element and return
}
}
Use pages in your tests, do not work with WebDriver directly.
#Test
public void someTest() {
Page page = new Page(driver);
page.doStep1();
page.doStep2();
assertEquals(page.getSomeData(), "Some expected result", "Optional Error message");
}
And..
Start from creating a test scenario code, create test steps, which you like to have (methods, which will be underlined in IDE as non-existing), and think about the data, which you like to check.
Then just implement all the non-existing methods you need in Page classes.
Maybe it sounds complicated, but try to start from simple test scenarios and it should become more clear after some practice.
I had the same issue. If this is a Java project, with Eclipse and Intellij IDEA, you can right click the project and select Convert to Maven. From there, it's fine adjustments (versions) in the POM file.

Create a test base page for calling desired capabilities

I was hoping for some help setting up my test frame work as its causing me issue. Im currently using zalenium in conjunction with my selenium tests. Currently im setting the desired capabilities in the #BeforeTest section of my tests:
#BeforeTest
#Parameters("browser")
public void setup(String br)throws MalformedURLException {
de = new DesiredCapabilities();
if (br.equals("chrome")) {
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.CHROME);
de.setCapability(CapabilityType.PLATFORM_NAME, org.openqa.selenium.Platform.LINUX);
}
else if(br.equals("firefox")){
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.FIREFOX);
de.setCapability(CapabilityType.PLATFORM_NAME, Platform.LINUX);
}
URL url = new URL("http://localhost:4444/wd/hub");
driver = new RemoteWebDriver(url,de);
driver.get(URL);
This allows me to run my testing in the docker environment and not on my local machine and is working correctly.
However i would like to create a base for these capabilities so i don't have to keep stating the desired capabilities for each test.
I want to do this also because I would like to set up separate classes for each page. Currently when i try this im getting a null pointer exception because the driver isnt declared. I tried to inject the Remote webdriver like so:
#Test
public void loginTest( RemoteWebdriver driver){
WebElement user_account_menu = driver.findElement(By.cssSelector("[data-user-account-settings-button]"));
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.elementToBeClickable(user_account_menu));
user_account_menu.click();
System.out.println("Login clicked successfully");
}
Im receiving the error: Cannot inject #Test annotated Method [loginTest] with [class org.openqa.selenium.remote.RemoteWebDriver
So im basically trying to figure out how i can set up these capabilities for the driver in a class and then extend them onto my tests.
From this link, it appears, you're not adding the right annotation, namely #Parameters.
However, in some other testing frameworks, it's not typical to be able to pass in variables that aren't determined at runtime, i.e. variables for objects like RemoteWebdriver won't work, but variables for strings or ints will work. From this link, it appears that what you're trying to accomplish is doable. But I recommend the following approach.
Have an enumeration:
enum BrowserType
{
Chrome, Firefox, IE;
}
Have your test base page:
public class BrowserSetup
{
public static RemoteWebdriver Initialize(string browserType)
{
switch (browserType)
{
case BrowserType.Chrome:
// set chrome capabilities and initialize your browser here
break;
case BrowserType.Firefox:
// set firefox capabilities and initialize your browser here
break;
default:
// set default case, or fail if browser type doesn't match
break;
}
}
Then, from you're test class, assuming you have a private RemoteWebdriver driver initialized somewhere in the test class, you can do the following:
#BeforeTest
#Parameters("Chrome")
public void setup(String browserType) throws MalformedURLException
{
driver = BrowserSetup.Initialize(browserType);
}

Dependence injection to initialize driver in cucumber project and use hook without terminating session

I`m trying to implement cucumber with Page Object Model and i faced couple problems and have lots of questions.
My iOS app is not that complex, but i still want to orginize all stepdefs and features to correspond with pages from POM. So i will have multiple stepdefs and runners. What is best practice to organize all of it ? I tried Pico DI, but wasn`t even able to pass my driver instance through it.( If you can, please provide structure solution)
Since its native iOS - Im not going to close app after every scenario( it will take forever). But I still want to keep features DRY for re-usability
What would be the best way to create one appium driver instance and never create another until feature is executed? I understand i just need to add driver.quit in the last step. What i`m straggling with is to use same driver throughout all project(Pages, Stepdefs) (see code)
Im going to run tests with TestNg and wonder if #Before #After annotations still work in stepdefs or it`s better to avoid them ?
The MAIN question : Dependency Injection with pico. Since all my tests are acceptance (end to end feature tests) i wonder if it is good idea to create one InjectionSetUp class which will contain all my pages AND driver
Driver manager
public class IOSDriverManager {
public static ThreadLocal<IOSDriver<IOSElement>> webDriver = new ThreadLocal<IOSDriver<IOSElement>>();
public static DesiredCapabilities getIOSCapsLocal() {
DesiredCapabilities caps = new DesiredCapabilities();
//My caps
return caps;
public static void createThreadLocalWebDriver() {
IOSDriver<IOSElement> driver = null;
//try catch
driver = new IOSDriver<IOSElement>(new URL(APPIUM_SERVER_URL), getIOSCapsLocal());
//try catch
webDriver.set(driver);
}
public static IOSDriver<IOSElement> getThreadLocalDriver() {
IOSDriver<IOSElement> driver = webDriver.get();
if (driver == null) {
createThreadLocalWebDriver();
driver = webDriver.get();
}
return driver;
}
BasePage
public class BasePage {
IOSDriver<IOSElement> Driver;
public BasePage(IOSDriver<IOSElement> driver) {
initElements();
Driver = driver;
}
private void initElements() {
PageFactory.initElements(new AppiumFieldDecorator(getDriver()),this);
}
protected IOSDriver<IOSElement> getDriver() {
return IOSDriverManager.getThreadLocalDriver();
}
}
AnyPage
public class BiosurveyPage extends BasePage {
public BiosurveyPage(IOSDriver<IOSElement> driver) {
super(driver); // Appiumfield decorator is applied by BasePage constructor
//Is it better to just use getDriver() method throughout all pages instead of declaring driver again ?
}
Finaly StepDefs
public class newSignUpFlowTest{
protected IOSDriver<IOSElement> getDriver() {
return IOSDriverManager.getThreadLocalDriver();
}
LoginPage poLogin = new LoginPage(getDriver());
SignUpPage poSignup = new SignUpPage(getDriver());
// 10+ pages
#Given("I am on Login Page")
public void iAmOnLoginPage() {
assertThat("ON LOGIN PAGE",poLogin.isLoginScreen(),equalTo(true));
}
#When("I tap on Sign Up Link")
public void iTapsOnSignUpLink() {
poLogin.clickSignUp();
}
// 20+ methods for this feature
UPDATE
I fixed everything and able to run tests. Now my question is - does my framework look decent ? I dont have any real life experience prior to this. So can someone just approve me and suggest enhancements to keep up with best industry practices ? I know this post might piss some people off, but i dont know where else to communicate this as i don`t have any friends in the QA field and working remotely
When using dependency injection you want to let your DI system do all the heavy lifting. So your step definitions have a constructor dependency on your page objects.
public class NewSignUpFlowStepDefinitions {
private final LoginPage poLogin;
private final SignUpPage poSignup;
NewSignUpFlowStepDefinitions(LoginPage poLogin, SignUpPage poSignup) {
this.poLogin = poLogin;
this.poSignup = poSignup;
}
#Given("I am on Login Page")
public void iAmOnLoginPage() {
assertThat("ON LOGIN PAGE", poLogin.isLoginScreen(), equalTo(true));
}
#When("I tap on Sign Up Link")
public void iTapsOnSignUpLink() {
poLogin.clickSignUp();
}
}
You page objects can't have a constructor dependency on IOSDriver<IOSElement> because PicoContainer can only create dependency chains that don't end with empty constructors. So instead we use the IOSDriverManager here.
public class BiosurveyPage extends BasePage {
public BiosurveyPage(IOSDriverManager driverManager) {
super(driverManager);
}
}
In your BasePage you then unpack the webdriver from the driver manager.
public abstract class BasePage {
private IOSDriverManager driverManager;
public BasePage(IOSDriverManager driverManager) {
this.driverManager = driverManager;
initElements();
}
private void initElements() {
PageFactory.initElements(new AppiumFieldDecorator(driverManager.getDriver()), this);
}
protected IOSDriver<IOSElement> getDriver() {
return driverManager.getDriver();
}
}
Then in the IOSDriverManager you can keep a reference to the webdriver and create it as needed. If you are certain it is safe to share your webdriver between scenarios you can still use a ThreadLocal here.
public class IOSDriverManager implements Disposable{
private IOSDriver<IOSElement> webDriver;
private DesiredCapabilities getIOSCapsLocal() {
DesiredCapabilities caps = new DesiredCapabilities();
return caps;
}
private void createWebDriver() {
webDriver = new IOSDriver<IOSElement>(new URL(APPIUM_SERVER_URL), getIOSCapsLocal());
}
public IOSDriver<IOSElement> getDriver() {
if (webDriver == null) {
createThreadLocalWebDriver();
}
return webDriver;
}
#Override
public void dispose() {
// Stop webdriver ehre
}
}
Note that Disposable adds the dispose method which will let you clean up your driver a after each scenario. Either to dispose it entirely or to reset it to some known state.
http://picocontainer.com/lifecycle.html

Set WebDriver in a pageObject class and then call it in your testcase

Can I write a method for WebDriver in a pageObject class, and then call the method in my testcase class?
public class setupMethods {
static WebDriver driver;
public static void setup(String browser) throws Exception{
//Check if parameter passed from TestNG is 'firefox'
if(browser.equalsIgnoreCase("firefox")){
//create firefox instance
System.setProperty("webdriver.gecko.driver", "U:\\path\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
}
//Check if parameter passed as 'chrome'
else if(browser.equalsIgnoreCase("chrome")){
System.setProperty("webdriver.chrome.driver", "U:\\selenium\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
}
//Check if parameter passed as 'Edge'
else if(browser.equalsIgnoreCase("IE")){
System.setProperty("webdriver.ie.driver","U:\\path\\IEDriverServer.exe");
InternetExplorerDriver driver;
driver = new InternetExplorerDriver();
}
else{
//If no browser passed throw exception
throw new Exception("Browser is not correct");
}
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
}
This is a method that I would like to call in my test case class, but when I run the test it starts Chrome (or any other browser), but then returns java.lang.NullPointerException when it comes to the line of code where the driver needs to maximize the window driver.manage().window().maximize();
You're having local variable driver in your method, that's why you're getting this exception.
Remove type declaration for driver in method body.
Here is the Answer to your Question:
Answering straight, Yes, you can write a method for WebDriver in a pageObject class, and then call the method in your testcase class
You need to consider certain changes in your code as follows:
As you need to call the method in my testcase you need to get back the WebDriver instance as a return. So change the method from public static void setup(String browser) to public static WebDriver setup(String browser)
As you have already declared the instance of the WebDriver as in static WebDriver driver;, so you no more need to mention the Class name WebDriver again while initializing the driver as in WebDriver driver = new FirefoxDriver();
Functionally, this is causing you NullPointerException in your code.
There is no best practice but I would prefer to check the passed parameter String browser in if loops only.
Once you have completed all the actions within this class, return the WebDriver instance.
Your own code with some simple tweaks will look like:
public class setupMethods
{
static WebDriver driver;
public static void setup(String browser) throws Exception
{
//Check if parameter passed from TestNG is 'firefox'
if(browser.equalsIgnoreCase("firefox"))
{
//create firefox instance
System.setProperty("webdriver.gecko.driver", "U:\\path\\geckodriver.exe");
driver = new FirefoxDriver();
}
//Check if parameter passed as 'chrome'
if(browser.equalsIgnoreCase("chrome"))
{
System.setProperty("webdriver.chrome.driver", "U:\\selenium\\chromedriver.exe");
driver = new ChromeDriver();
}
//Check if parameter passed as 'Edge'
if(browser.equalsIgnoreCase("IE"))
{
System.setProperty("webdriver.ie.driver","U:\\path\\IEDriverServer.exe");
InternetExplorerDriver driver;
driver = new InternetExplorerDriver();
}
else
{
//If no browser passed throw exception
throw new Exception("Browser is not correct");
}
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
return driver;
}
}
Let me know if this Answers your Question.
I have resolved the issue that I had...after using code that #debanjanB suggested, and using constructor for a driver in page object classes by using
public name_of_pageobject_class(WebDriver driver){
this.driver = setupMethods.driver;
}
and calling driver in test cases classes as:
WebDriver driver = setupMethods.setup("chrome");
Please correct me if anything might be wrong....but this now works.

Multiple browser windows opening automatically in cucumber

I don't know why I am getting 2 firefox browsers opened for the follwoing example. Can some one please tell me what is wrong in below code. I am new to cucumber and I am trying to develop cucumber poc with page object model.
Feature file:
Scenario: Smoke test for application
Given I am on home page
Step Defination file:
public class HomePageSteps {
CustomerDetails customerDetails;
HomePage homePage=new HomePage();
public HomePageSteps(CustomerDetails customerDetails){
this.customerDetails=customerDetails;
}
#Before
public void environmentSteup(){
homePage.envSetup();
}
#Given("^I am on home page$")
public void i_am_on_home_page() throws Throwable {
homePage.openURL();
}
}
Actual implementation of Step definition file:(HomePage.java)
public class HomePage extends BasePage{
public void openURL() {
driver.get("https://applicationURL.aspx");
System.out.println("I am on home page executed");
}
public void envSetup() {
driver=new FirefoxDriver();
driver.manage().window().maximize();
}
}
BasePage.java
public abstract class BasePage {
protected WebDriver driver=new FirefoxDriver();
}
CustomerDetails.java
public class CustomerDetails {
private String mdn=null;
private String Fname=null;
private String Lname=null;
public String getMdn() {
return mdn;
}
public void setMdn(String mdn) {
this.mdn = mdn;
}
}
2 firefox browsers are opened:
First it opens a blank browser. Later it opens another browser and in this browser it opens the application URL.
You have two calls to open browser windows...
Once in the sub-class in envSetup() - driver=new FirefoxDriver();
And in the super class driver variable declaration with initialization - protected WebDriver driver=new FirefoxDriver();
You have to remove one of them, no need for the super class one... This is the one giving you the blank window
Refer to this page. Your maximize() call in envSetup() might be doing more than you think
In selenium webdriver what is manage() [driver.manage()]
edit:
You also do not need to instantiate a new FirefoxDriver() outside of BasePage as you have already instantiated a driver field with that object. Anything extending BasePage will have access to that driver field. It is not a problem that you're doing this, it is just extraneous code that doesn't need to be there

Categories