How do I declare a driver as global? - java

Here is how I declare firefox driver:
public static WebDriver driver = new FirefoxDriver();
I place the code above outside main and within my class (global)
Here is how I declare chrome driver:
System.setProperty("webdriver.chrome.driver", "/path/xxx/xxx/xx");
WebDriver driver = new ChromeDriver();
I place the code above in main
Here is the issue:
I want to make the ChromeDriver as a global but I NEED to set the property before doing so. But I place the System.setProperty("xx","xx"); within the main body. Cuz it gives error when placed outside.
Here is a user trying to do the same thing as me. Trying to run different browsers using the same driver : How to run Selenium tests in multiple browsers for cross-browser testing using Java?
The answer is involves declaring the driver in the main body and not as a constant before.
My issue: All functions need driver declaration from before. Calling functions which use driver. If I declare driver in main, I need to continuously pass it as a parameter to all the functions. I do not wish to do that. Here is an example function
public static void a(){
driver.findElement(By.id("hi"));
}

How about something like:
class SomeTest {
static WebDriver driver;
public static void main(String[] args) {
System.setProperty("key", "value");
driver = new ChromeDriver();
}
public static void a() {
driver.findElement(By.id("hi"));
}
}

Related

How to initialize a driver object so it can be used by all classes

How to initialize the driver so it can be used by all classes
Hi All,
I am writing a test automation framework in JAVA using Appium, Selenium and Cucumber.
I start off by declaring an Appium Driver in one of my test step files and then this gets cast to an Android Driver or iOS Driver depending on the app under test.
I need some help please - I need all of my class files to have access to this instance of the driver but I’m not sure how to do this. The test is driven from the feature file and some of the test steps are in different class files so how can they all access this instance of the driver?
Thanks
Matt
You can make an initialising method in the class where all the other config setup can be done and then you can make an instance of that class to call the getDriver method.
For example:
public class initialiseDriver{
private static AppiumDriver<MobileElement> driver;
public AppiumDriver<MobileElement> getDriver() throws IOException {
if (PLATFORM_NAME.equals("Android")) {
// setup the android driver
} else if (PLATFORM_NAME.equals("iOS")) {
// setup the ios driver
}
return driver;
}
}
You can just call this method where you want to use the driver. Ideally, you should initialise the driver by calling this method in the #BeforeSuite/#BeforeClass method, so that you don't need to call this method everytime you start your script as it would be called implicitly with the #BeforeSuite/#BeforeClass.
you can define your AppiumDriver as static
public class AppiumHelper(){
public static AppiumDriver<MobileElement> driver;
public void setupDriver(){
//define your DesiredCapabilities
//initialize your driver
}
Then you can use your driver in your test method like
public void test1(){
MobileElement element= AppiumHelper.driver.findElementById("elements id");
}
The serenity PageObject class provides an inbuilt getDriver() method which you can call wherever you want to initialize the driver(preferably in the test classes). Avoid trying to initialize the driver in any of your step definations/step libraries(Managing using #Managed annotation) else it will throw a :
null pointer exception.

While using TestNG if we define WebDriver driver; globally then without creating object of that class how we are able to use driver under a method?

The code is working fine but why? without creating object of class Testing123 How we are able to access that driver?
public class Testing123 {
WebDriver driver ;
#Test
public void test1() {
driver = new ChromeDriver();
driver.get("http://google.com");
}
}
TestNG framework is taking care of creating an instance of your test class behind the scenes. Basically by annotating your method with '#Test' the annotation processor associate the class to the test runner. For further info look at: http://makeseleniumeasy.com/2018/06/08/testng-tutorials-21-why-dont-we-require-a-main-method-in-testng-class-for-execution-of-methods/

Extend Multiple Classes in Java while Retaining Functionality of each Class

I'm using WebDriver (Selenium) and I want to add custom methods to WebDriver such as driver.performCustomAction().
Being that I could instantiate an instance of FirefoxDriver or ChromeDriver I cannot simply extend FirefoxDriver bec I would not be able to use the functionality with Chrome Driver.
Tech I could create a new class and pass an instance of WebDriver to the constructor (so it could be either FF or Chrome) but then I would be unable to perform all of the non custom actions of each class such as findElements(), getText() on the new object.
In other words, if my new class is called WrappedWebDriver and I instaniate a new instance of it as follows:
WebDriver FFDriver = new FirefoxDriver();
WrappedWebDriver WDriver = new WrappedWebDriver(FFDriver);
I will be able to call WDriver.performCustomAction() but I will not be able to call WDriver.findElement() or any of the other methods defined in the FirefoxDriver class (or the actions I could perform using FFDriver ).
How can I add new methods that apply to both FirefoxDriver and ChromeDriver without writing it twice while retaining all functionality of each respective class?
P.S: I know Java doesn't allow multiple inheritance is there some other way around it?
I think you can create Wrapper class which will hold instance of the Webdriver and you will wrap methods of webdriver which u want to support. You can do it for instance like this:
public class WrappedWebDriver {
public WebDriver driver;
public WrappedWebDriver(WebDriver driver){
this.driver = driver
}
public WebElement find(By by){
//your customization code
return driver.findElement(by);
}
public void setText(By by, String text){
//your customization code
driver.findElement(by).sendKeys(text)
}
public void performCustomAction(){
//your customization code
}
}
You can customize Webdriver standard methods by adding some functionality in wrapped methods. By making driver public, you give user an option to choose beetween using standard driver methods or your customized methods.
There is nice and useful Wrapper API for selenium named Conductor. https://github.com/conductor-framework/conductor. There you can find more complex example how to wrap WebDriver.
You perhaps need to be Extending EventFiringWebDriver. This class by its very behavior is composite in nature (Its created by taking a reference to an existing webdriver instance) and it was originally designed to be used for tapping into the before/after events of all webdriver originating actions. But it can very well suite your purpose.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class WrappedDriver extends EventFiringWebDriver {
public WrappedDriver(WebDriver driver) {
super(driver);
}
public void performCustomAction(){
//your customization code
}
}
So in essence you first build a decorator that implements all the interfaces that an actual RemoteWebDriver sub-class would implement and then have your customized class extend the decorator. The decorator class here in this case is EventFiringWebDriver

Dynamically implementing interface?

I was thinking on the following example for taking a screenshot in WebDriver2:
WebDriver driver = new FirefoxDriver();
driver.get("http://www.google.com/");
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
When a class implements an Interface, the class must implement the defined the methods in the interface, right?
So, how come during runtime we implement TakeScreenshot interface, without implementing the logic in getScreenshotAs method before that?
I tried to simulate it this way:
interface TakeScreenShot{ public void getScreenshotAs(); }
class WebDriver
{
public static void main (String[] args) throws java.lang.Exception
{
WebDriver driver = new WebDriver();
((TakeScreenShot)driver).getScreenshot();
}
}
I ran it in Ideone and I am getting a runtime error:
Runtime error time: 0.05 memory: 711168 signal:-1
So, how does it work in WebDriver?
In your first example, WebDriver is an interface - and interface that doesn't extend TakesScreenshot. So if you have a variable driver of the declared type WebDriver, you can't call methods on that variable that are in TakesScreenshot.
But the variable driver points to an actual object which has an implementation class - FirefoxDriver. And FirefoxDriver does implement the TakesScreenshot interface.
There are several other ways in which you can invoke the getScreenshotAs method:
Example 1:
change the declared type of driver to FirefoxDriver which does
implement TakesScreenshot:
FirefoxDriver driver = new FirefoxDriver();
driver.get("http://www.google.com/");
File scrFile = driver.getScreenshotAs(OutputType.FILE);
Example 2:
cast WebDriver driver to FirefoxDriver instead of
TakesScreenshot - because FirefoxDriver implements
TakesScreenshot you can call methods from the latter interface
directly through type FirefoxDriver.
WebDriver driver = new FirefoxDriver();
driver.get("http://www.google.com/");
File scrFile = ((FirefoxDriver)driver).getScreenshotAs(OutputType.FILE);
Now in your second example, you have a class WebDriver (not an interface) and that class doesn't implement TakesScreenshot so you can't cast it to that type.
This is because the interface WebDriver is not a subclass of TakesScreenshot. However, the object being assigned to driver in your first snippet is of type FirefoxDriver which is both a WebDriver and TakesScreenshot.
The technique being done in your first snippet is "programming to an interface". There are a lot of duplicate questions regarding that, but this answer is the simplest.
You basically do that when you only care that driver is a WebDriver instance, and you don't care whether it is an OperaDriver, ChromeDriver, etc. This is useful when you want to use WebDriver methods without worrying about how that functionality is implemented.
Now casting it to TakesScreenshot means that whatever driver variable is, you are hoping that it also implements TakesScreenshot. Because if it does not, you will encounter a ClassCastException at runtime, as you observed.

How to switch instances in WebDriver

Hi I have following setup with my current framework,
ClassA
{
//Which Receives Selenium WebDriver call the 'driver' object reference to manipulate the locators in UI
public WebDriver get()
{
return MainClass.driver;
}
}
MainClass
{
public static Webdriver driver;
method A()
{
//which uses Firefox instance and it is passed to ClassA to operate
driver = new FirefoxDriver();
}
methodB()
{
//which creates new instance of Chrome
driver = new ChromeDriver();
}
}
What I wanted to do is once I call methodB() the instance of Chrome is created but once it is done I want to resume back to firefox instance that is available or invoked before chrome run, but with my approach since I am referring the same webdriver object the old firefox reference is getting deleted.
Any Suggestions ?
PS: Please forgive my bad code conventions I followed
Simplest solution would be to create seperate objects for FF and Chrome. Modify get method to take a parameter(browserType) and then return the correspoding object.
Why are you switching browsers?
You might want to see a different approach to your situation. I believe that if you have to use 2 browsers you are most likely trying to pass some info from one to the other. Here is how I see it:
ClassA
{
//Which Receives Selenium WebDriver call the 'driver' object reference to manipulate the locators in UI
public WebDriver get()
{
return MainClass.driver;
}
}
MainClass
{
public static Webdriver currentBrowser, firefoxInstance chromeInstance;
firefoxInstance = new FirefoxDriver();
chromeInstance= new ChromeDriver();
currentBrowser = firefoxInstance; //if you want start out with Firefox
currentBrowser()
{
return currentBrowser;
}
switchBrowser(Cookies passingInfo) //passingInfo could be like cookies but also just current page etc...
{
if(currentBrowser==firefoxInstance)
{
chromeInstance.cookies()=passingInfo; // this is definitely not the correct way of passing cookies in Selenium but you get my point
currentBrowser=chromeInstance;
}
else
{
firefoxInstance.cookies()=passingInfo;
currentBrowser=firefoxInstance;
}
}
}
Of course there are more than one way of doing this and it depends on what you end goal is. But keep in mind that some websites are designed differently depending on the user agent of the browser connecting to them and that might cause your code to crash (like i just experienced 2 minutes ago.) I recommend sticking to one web browser if you can.
PS: Please forgive me using your bad code conventions :)

Categories