Where is getScreenshotAs method defined?

7.2k Views Asked by At

I was going through Screenshot code in selenium. Below is the code for it :

File src = ((TakesScreenshot)driver).getScreenshotAs(OutputType.File);

In the above line, TakesScreenshot is an interface and getScreenshotAs is a method. So what I understand from this is, we are typecasting driver into TakesScreenshot interface which essentially means that our driver will behave like TakesScreenshot from now after which getScreenshotAs method will be executed.

My question here is that an interface can only have abstract methods. So, how is getScreenshotAs method is executed through TakesScreenshot interface as it would not have any definition of this method. More precisely, where is getScreenshotAs method defined and how does the above line of code works ?


Adding a few more details :

TakesScreenshot --> an interface

driver --> instance of ChromeDriver class (WebDriver driver = new ChromeDriver())

getScreenshotAs --> method in TakesScreenshot interface.

The above code is used to take screenshot of webpages in selenium.

3

There are 3 best solutions below

6
On

Well, as I understand, it's not actually a selenium-specific question, but basic Java question.

The meaning of the expression you provided:

((TakesScreenshot) driver).getScreenshotAs(OutputType.File)

is following: no matter what is the type of driver variable, in this line we are sure that it implements TakesScreenshot interface which has getScreenshotAs method. So we're casting type to TakesScreenshot and call getScreenshotAs method on the driver object. The implementation of this method is inside real driver class whichever it is.

To give you an example which will be really close to the question code (I made this method to accept Object so we really need to cast o to the target interface. Don't do it in real code):

public void log(Object o) {
  ((Printable) o).print();
}

where Printable is some interface with method print:

public interface Printable {
  void print();
}

so if we have some implementation of Printable like

public class Greeting implements Printable {

  @Override
  public void print() {
    System.out.println("Hello, username");
  }

}

we can call

log(new Greeting())

which result in line "Hello, username"

Edit:

As I can see in JavaDoc to selenium, WebDriver interface does not extend TakesScreenshot interface. So if the type of driver variable is WebDriver interface you have to cast it. WebDriver driver = new ChromeDriver() - there is only reference of type WebDriver for compiler. Despite the fact that real class is ChromeDriver compiler doesn't know it. So in this case in order to call getScreenshotAs method you have to cast driver to TakesScreenshot (and it's safe as driver is instance of ChromeDriver which implements both WebDriver and TakesScreenshot interfaces). Only after that you can call getScreenshotAs method from TakesScreenshot interface.

Well, as I understand, it's not actually a selenium-specific question, but basic Java question.

The meaning of the expression you provided:

((TakesScreenshot) driver).getScreenshotAs(OutputType.File)

is following: no matter what is the type of driver variable, in this line we are sure that it implements TakesScreenshot interface which has getScreenshotAs method. So we're casting type to TakesScreenshot and call getScreenshotAs method on the driver object. The implementation of this method is inside real driver class whichever it is.

To give you an example which will be really close to the question code (I made this method to accept Object so we really need to cast o to the target interface. Don't do it in real code):

public void log(Object o) {
  ((Printable) o).print();
}

where Printable is some interface with method print:

public interface Printable {
  void print();
}

so if we have some implementation of Printable like

public class Greeting implements Printable {

  @Override
  public void print() {
    System.out.println("Hello, username");
  }

}

we can call

log(new Greeting())

which result in line "Hello, username"

Edit:

As I can see in JavaDoc to selenium, WebDriver interface does not extend TakesScreenshot interface. So if the type of driver variable is WebDriver interface you have to cast it. WebDriver driver = new ChromeDriver() - there is only reference of type WebDriver for compiler. Despite the fact that real class is ChromeDriver compiler doesn't know it. So in this case in order to call getScreenshotAs method you have to cast driver to TakesScreenshot (and it's safe as driver is instance of ChromeDriver which implements both WebDriver and TakesScreenshot interfaces). Only after that you can call getScreenshotAs method from TakesScreenshot interface.

WebDriver driver = new ChromeDriver();
// driver.getScreenshotAs(OutputType.File); // compilation error as there is no method getScreenshotAs in WebDriver interface
((TakesScreenshot) driver).getScreenshotAs(OutputType.File); // ok after explicit casting
0
On

The method getScreenshotAs is implemented in RemoteWebDriver class. You can't use it from your driver instance because you used the WebDriver interface, which doesn't extends TakesScreenshot interface, to create the driver instance.

Casting to (or using in the first place) RemoteWebDriver or ChromeDriver will also allow you to use the method.

File src = ((RemoteWebDriver)driver).getScreenshotAs(OutputType.File);
0
On

First of all let me break down you code for an explaination:

File src = ((TakesScreenshot)driver).getScreenshotAs(OutputType.File);

into these 2 lines:

TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);

Analysis

As per the documentation, TakesScreenshot is an interface in Selenium within the org.openqa.selenium package. public interface TakesScreenshot indicates that a driver that can capture a screenshot and store it in the below mentioned different ways. public interface TakesScreenshot has a known Subinterface as WebElement. The known Implementing Classes are :

  1. ChromeDriver
  2. FirefoxDriver
  3. InternetExplorerDriver
  4. EdgeDriver
  5. OperaDriver
  6. RemoteWebDriver
  7. RemoteWebElement`
  8. SafariDriver
  9. EventFiringWebDriver

In the first line we are initializing an instance of TakesScreenshot as ts and casting the WebDriver instance i.e driver to that instance.

In the second line we are trying to capture the screenshot and storing it in a specified location. For WebDriver extending TakesScreenshot, this makes a best effort depending on the browser (casted) to return the following object in order of preference :

  1. Entire page
  2. Current window
  3. Visible portion of the current frame
  4. The screenshot of the entire display containing the browser

getScreenshotAs() method returns the Object in which is stored information about the screenshot is contained. On failure java.lang.UnsupportedOperationException which means the underlying implementation does not support screenshot capturing mechanism.

You can find the details documentation here.


Reference

You can find a detailed discussion in How to take screenshot with Selenium WebDriver