Extend Multiple Classes in Java while Retaining Functionality of each Class - java

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

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/

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.

Text check in every page (Selenium)

Using Java, I am trying to write a general check for a particular text on every page in a web application that existing tests visit. Instead of having to write it on each and every page individually, is it possible to do in one place at a high level (may be in the base class)?
public class BaseClassForUiTest {
public BaseClassForUiTest() {
...
}
public void test() throws Exception {
boolean isNewPage = checkIfNewPage();
if (isNewPage)
// perform a text check on the page
}
}
Every test extends from BaseClassForUiTest and overrides the test() method.
Instead of having to write it on each and every page individually, is it possible to do in one place at a high level (may be in the base class)?
Yes, it is possible by implementing WebDriverEventListener into BaseClassForUiTest and override event handler methods to handling the appropriate WebDriver events according to need in one place.
Here every method corresponds to an event. According to your requirement you need to handle afterNavigateTo() method. This one is called every time the navigate to a page is completed.
You have to do perform a text checker on the page code in this method so that your code is executed every time the page navigates to some other page.
public class BaseClassForUiTest implements WebDriverEventListener
{
---------
---------
public void afterNavigateTo(String arg0, WebDriver arg1) {
// perform desire text checker stuff on the page here
}
}
Now Create Event Throwing WebDriver to perform your test :-
Create a regular WebDriver.
FirefoxDriver driver = new FirefoxDriver();
Now create an EventThrowingWebDriver using our regular WebDriver created above.
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
Create an instance of your eventHandler class and register it for events using the register method of EventFiringWebDriver object created above as :-
BaseClassForUiTest handler = new BaseClassForUiTest();
eventDriver.register(handler);
Full code :-
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class BaseClassForUiTest {
public void test() throws Exception {
FirefoxDriver driver = new FirefoxDriver();
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
BaseClassForUiTest handler = new BaseClassForUiTest();
eventDriver.register(handler);
eventDriver.get("your url");
//Now do your further stuff
}
}
As for me, you'd better create a separated test class for this check using Parameterized or JUnitParams and give it a urls to run where as parameters, but it depends on what is your common approach to running tests (we run all testpack at the same time, so it's a solution for us in this situation).
Also it is seems like well-logically-separated solution
If you are going to use this check as an assertion you can rewrite your current code for this case and call for it in #Before block (but it is still not a good solution, in my opinion)

Whats the best way to initialize and use a WebDriver PageObject using PageFactory?

With WebDriver and PageFactory, using Java we are implementing a new automation project, and we've been experimenting with various ways of having PageObjects created. We're torn on a few different ideas, and want to make sure we don't work ourselves into a corner.
Is it best to, as documented in the WebDriver documentation, provide an initialized WebDriver to a PageFactory, along with the class template to create a new PageObject?
driver.get(URL);
PageObject page = PageFactory.initElements(driver, PageObject.class);
// elsewhere
class PageObject {
private WebDriver driver;
public PageObject(WebDriver driver) {
this.driver = driver;
this.validateUrl();
}
public void validateUrl() throws Exception {
if (!driver.getUrl().equals(url)) {
throw new Exception("URL not valid");
}
}
}
However, since the PageObject knows a lot about itself, such as perhaps its URL, can we not have the Page Object do the work?
PageObject page = new PageObject(driver);
page.goToUrl();
// elsewhere
class PageObject {
private WebDriver driver;
private String url;
public PageObject(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void goToUrl() {
driver.get(url);
}
}
I suppose I don't see much of an advantage to having the PageFactory do the instantiation versus just initialization, however I don't want to stray from the standards setup by the architects if there's a reason for it.
Thanks
One of the advantage of Page Factory:
Scenario:
In your application, you are having 100 fields in a page. The same page is called for 50 times.
If this type of scenario is done by using Page Object means, it will find each element again and again. There may be a chance for degradation of the performance.
If the same scenario is done by using Page Factory means, it will find the elements only for the first time, and then it will take from the cache. By this the performance is increased.
The best way is to let the frameworks like Geb and Thucydides abstract out the PageObjects and their initialization. I have been using Geb + Spock BDD combination for this and the results so far has been excellent.

Categories