In interview a frequently asked interview question is “Why do we upcast browser class object to WebDriver? or why we do not upcast to RemoteWebDriver class instead of WebDriver?” and we give all sort of answers.
First of all this question is asked with assumption that we do not have any option except upcast. Is it so? Absolutely not. It completely up to you whether you upcast or not and if you do then to which level? Question should be “Why should we upcast it or what are advantages if you upcast?
Before we start thinking this particular topic only with respect to Selenium WebDriver-Java, let’s learn core Java concepts i.e. Inheritance and upcast.
I define an Interface in Java as “A contract. I want something to do but I do not know how to do the same. I need a class or multiple classes to achieve that.“. This may not should exactly with purpose of Interface but at high level this we do.
“Amod” ( My name ) wants to perform swapping of two numbers in Java but he does not know the logic to do. So he has created an interface as below:-
package InheritanceExamples; public interface WhatAmodWantsButDontKnowHowToDo { void swapTwoIntVariables(int a, int b); }
Amod has a best friend “John” who has average programming knowledge and promises to give implementation of Amod’s method. Amod is so happy now. John gives implementation as below:-
package InheritanceExamples; public class JohnKnowsImplementation implements WhatAmodWantsButDontKnowHowToDo{ @Override public void swapTwoIntVariables(int a, int b) { System.out.println("--------------Before swap------------"); System.out.println("a :" + a); System.out.println("b :" + b); /// Swap logic using temporary int c = a; a = b; b = c; System.out.println("--------------After swap------------"); System.out.println("a :" + a); System.out.println("b :" + b); } }
Now Amod creates a usage utility to run his incomplete method as below:-
package InheritanceExamples; public class AmodUsesImplementationGivenByJohn { public static void main(String[] args) { JohnKnowsImplementation johnKnowsImplementation = new JohnKnowsImplementation(); johnKnowsImplementation.swapTwoIntVariables(10, 20); } }
Output :-
--------------Before swap------------ a :10 b :20 --------------After swap------------ a :20 b :10
Amod is happy now and everywhere he is appreciating John. One day he met his best mentor Mukesh who is a great developer. He says to Amod that he can provide a more optimal and memory efficient implementation of swapping method. Amod is more happy now and asked for the implementation from Mukesh. Mukesh gives implementation as below:-
package InheritanceExamples; public class MukeshKnowsOptimalImplementation implements WhatAmodWantsButDontKnowHowToDo{ @Override public void swapTwoIntVariables(int a, int b) { System.out.println("--------------Before swap------------"); System.out.println("a :" + a); System.out.println("b :" + b); /// Swap logic without using temporary a = a+b; b = a -b; a = a-b; System.out.println("--------------After swap------------"); System.out.println("a :" + a); System.out.println("b :" + b); } }
Now Amod creates a new usage utility class so that he can use Mukesh’s implementation.
package InheritanceExamples; public class AmodUsesImplementationGivenByMukesh { public static void main(String[] args) { MukeshKnowsOptimalImplementation mukeshKnowsOptimalImplementation = new MukeshKnowsOptimalImplementation(); mukeshKnowsOptimalImplementation.swapTwoIntVariables(10, 20); } }
As Amod is using Mukesh’s implementation it makes John sad. He says to Amod, he is not doing good with him. Amod does not want to make sad any one. He is in tension now what to do. He is also thinking if there is another new implementation given by someone else again he needs to create a new usage class and same problem may arise again.
He found a solution in core concept of Interface, Inheritance and runtime method overriding as below:-
package InheritanceExamples; public class UpcastImplementation { public static void main(String[] args) { WhatAmodWantsButDontKnowHowToDo whatAmodWantsButDontKnowHowToDo; // Using John's Implementation whatAmodWantsButDontKnowHowToDo = new JohnKnowsImplementation(); whatAmodWantsButDontKnowHowToDo.swapTwoIntVariables(10, 20); // Using Mukesh's Implementation whatAmodWantsButDontKnowHowToDo = new MukeshKnowsOptimalImplementation(); whatAmodWantsButDontKnowHowToDo.swapTwoIntVariables(10, 20); } }
Amod has created a reference of his Interface as whatAmodWantsButDontKnowHowToDo. Since both John and Mukesh’s implementation class implement Amod’s interface, whatAmodWantsButDontKnowHowToDo can hold any object of implemented class. If there is further new implementations of Amod’s method, he does not need to create a new class anymore. When John’s comes to meet Amod then he sees his implementation is being used and same for Mukesh. ( Please assume the game :-p Hypothetical statement ).
Story over but did you get the point of story?
Implementation Classes of WebDriver Interface
Above classes in image are directly or indirectly implementations of WebDriver interface. In fact RemoteWebDriver is direct implemented class of WebDriver and remaining browser classes extends RemoteWebDriver. There is another update I can see in Selenium 4 that, a new class ChromiumDriver is introduced which extends RemoteWebDriver and ChromeDriver class extends ChromiumDriver now.
We know WebDriver is an interface and these implemented class complete it. Many implemented classes represent a browser. You can related WebDriver interface as WhatAmodWantsButDontKnowHowToDo interface and ChromeDriver , FirefoxDriver etc as MukeshKnowsOptimalImplementation and JohnKnowsImplementation.
Let’s talk with respect to Selenium WebDriver now
When we do not upcast to WebDriver
Suppose, you need to create a base class which need to be extended in every class and perform some actions. User should be able to launch any browser.
You will create a base class as below:
package InheritanceExamples; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.ie.InternetExplorerDriver; import org.openqa.selenium.safari.SafariDriver; public class WithOutUpcastToWebDriver { // Since user should be able to launch any browser , we need to create reference object of every browser type public static ChromeDriver cDriver; public static FirefoxDriver fDriver; public static InternetExplorerDriver iDriver; public static EdgeDriver eDriver; public static SafariDriver sDriver; // We need to keep adding browser driver reference variable }
You will create test scripts for chrome browser as below:-
package InheritanceExamples; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.openqa.selenium.OutputType; import org.openqa.selenium.chrome.ChromeDriver; import io.github.bonigarcia.wdm.WebDriverManager; public class ChromeBrowserUse extends WithOutUpcastToWebDriver { public static void main(String[] args) throws IOException { // Since you need to launch chrome browser , you need to use ChromeBrowser reference variable as below WebDriverManager.chromedriver().setup(); cDriver= new ChromeDriver(); cDriver.get("http://makeseleniumeasy.com/"); // Taking screenshot // To take screenshot no need to downcast to TakesScreenshot interface File screenshotSRC= cDriver.getScreenshotAs(OutputType.FILE); // Defining path and extension of image String path=System.getProperty("user.dir")+"/ScreenCapturesPNG/"+System.currentTimeMillis()+".png"; // copying file from temp folder to desired location File screenshotDest= new File(path); FileUtils.copyFile(screenshotSRC, screenshotDest); // Running javascript command // No need to downcast to JavascriptExecutor cDriver.executeScript("window.scrollBy(0,250)"); // Closing browser cDriver.quit(); } }
Similarly you need to do for other browsers as well. If a new browser comes in market, you need to add in base class first.
When we upcast to WebDriver:
We will create a very simple base class as below:
package InheritanceExamples; import org.openqa.selenium.WebDriver; public class WithUpcastToWebDriver { public static WebDriver driver; }
We will create scripts as below:
package InheritanceExamples; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.chrome.ChromeDriver; import io.github.bonigarcia.wdm.WebDriverManager; public class ChromeBrowserUseWithUpcast extends WithUpcastToWebDriver { public static void main(String[] args) throws IOException { WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(); driver.get("http://makeseleniumeasy.com/"); // Taking screenshot // To take screenshot we need to downcast to TakesScreenshot interface // down casting WebDriver to TakesScreenshot to use getScreenshotAs method. TakesScreenshot ts = (TakesScreenshot) driver; File screenshotSRC = ts.getScreenshotAs(OutputType.FILE); // Defining path and extension of image String path = System.getProperty("user.dir") + "/ScreenCapturesPNG/" + System.currentTimeMillis() + ".png"; // copying file from temp folder to desired location File screenshotDest = new File(path); FileUtils.copyFile(screenshotSRC, screenshotDest); // Running javascript command // Need to downcast to JavascriptExecutor JavascriptExecutor jse = (JavascriptExecutor) driver; jse.executeScript("window.scrollBy(0,250)"); // Closing browser driver.quit(); } }
In above approach, you do not need to change in base class if new browser comes. You have flexibility of using any browser with same reference.
Above question will not be answered completely if I do not tell you :-
- Why not upcast to SearchContext which is supermost interface?
- Why not upcast to RemoteWebDriver?
SearchContext is a parents interface of WebDriver and consists of only two methods findElement(By by) and findElements(By by). Obviosuly you can upcast to SearchContext but you need to downcast it to desired browser driver reference even to use a get() method. SearchContext reference will not have visibility of methods of implemented class.
package InheritanceExamples; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.OutputType; import org.openqa.selenium.SearchContext; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.chrome.ChromeDriver; import io.github.bonigarcia.wdm.WebDriverManager; public class UpcastToSearchContext { public static void main(String[] args) throws IOException { SearchContext searchContext; WebDriverManager.chromedriver().setup(); searchContext = new ChromeDriver(); // You need to upcast to use basic methods like get as well ChromeDriver driver = (ChromeDriver) searchContext; driver.get("http://makeseleniumeasy.com/"); // Taking screenshot // To take screenshot we need to downcast to TakesScreenshot interface // down casting WebDriver to TakesScreenshot to use getScreenshotAs method. TakesScreenshot ts = (TakesScreenshot) driver; File screenshotSRC = ts.getScreenshotAs(OutputType.FILE); // Defining path and extension of image String path = System.getProperty("user.dir") + "/ScreenCapturesPNG/" + System.currentTimeMillis() + ".png"; // copying file from temp folder to desired location File screenshotDest = new File(path); FileUtils.copyFile(screenshotSRC, screenshotDest); // Running javascript command // Need to downcast to JavascriptExecutor JavascriptExecutor jse = (JavascriptExecutor) driver; jse.executeScript("window.scrollBy(0,250)"); // Closing browser driver.quit(); } }
Now the last question, why not to upcast to RemoteWebDriver when all browser classes extends it. It is again a valid question and answer is you can do. It will not make much difference but what if a new class comes in place of RemoteWebDriver?? Your existing script may not be upgraded. Anyway chances of getting a replacement of RemoteWebDriver is very very less but still. Upcast to WebDriver and keep framework supportable for all browsers and remote executions as well.
You can download/clone above sample project from here.
If you have any doubt, feel free to comment below.
If you like my posts, please like, comment, share and subscribe.
#ThanksForReading
#HappyLearning
Find all Selenium related post here, all API manual and automation related posts here and find frequently asked Java Programs here.
Many other topics you can navigate through menu.
Appreciate your efforts and details explaination given.Can you create some blog series for Appium as well
Regards,
Bhushan
It’s a famous question and now I got t know how to answer it correctly. Thanks Amod.
Hey, could you please explain the statement: “It will problem to maintain a same reference of browser driver. You might get null pointer exception.?”
In the first part of the above question I see a line under the Dis Advantages – *You can not use methods defined in above classes in hierarchy.*
I would like to know in which context the above line is said, because when a class is extended, we can always use the methods defined in above classes or from the interfaces in hierarchy.
In case of multilevel inheritance, you may have different implementation of same method in two classes. If you don’t upcast, always last overridden method will be called.
Why it is run time polymorphism ??
Refer below link:
https://www.geeksforgeeks.org/dynamic-method-dispatch-runtime-polymorphism-java/
Hi Amod, Thanks for your valuable inputs – I thinks your detailed information is worth reading on each topic.
Currently I am stuck on this and if you mind letting me know. Here it is –
In Selenium – we have findElement() method that returns webElement which is an interface. So we usually store it under Webelement type of variable. I know at a very abstract level that this variable is referring to some object and I am not able to understanding the architecture as how we we are making the interface variable point to an object and hence achieving this execution.
It will be of great help if you can provide some code example on same.
Looking forward for your response.
Hello Pradeep,
Interface is a type which you can say a referenced variable. When you have a class named “Demo” and you write Demo d, it means you created a referenced variable of type Demo. findElement method returns something which can be stored in Type WebElement. And an interface type can hold a class object if there is relationship between them.
Thanks Pramod for response, well this is what my question is – What java concept is applied here such that we are making a method returning an interface (Findelement method in this case) – and in turn that interface reference variable points to class object. What phenomena is this?
Its magic of multilevel inheritance. Upcasting and down casting concepts are used here.
Thanks Amod – But I would like to make a request to cover it in detail if it is possible at your end in your future topics. This help help a lot.
Thanks Again !
Will post in Java section soon.
Well thats good topic and had same question in mind! Thanks for clarifying the concept! I had a doubt on Javascript executor.Why is it required?
Thanks Debanjan.
I will post on use of javascript in selenium soon.
just a TYPO in 2nd Example in comment line…//*”No”* //need to downcast to JavascriptExecutor
JavascriptExecutor jse = (JavascriptExecutor)_driver;
Will correct that. Thanks.
Good Example.
Thanks Gaurav.