0

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.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
brij
  • 331
  • 1
  • 5
  • 17
  • what is `TakesScreenshot ` and `driver`, you need to post at least part of the code – Ravi Oct 08 '17 at 09:30
  • What you actually want to know not clear – iamsankalp89 Oct 08 '17 at 09:31
  • 1
    dude if you know selenium, you would know this is the entire code for taking screenshot. – brij Oct 08 '17 at 09:32
  • But, dude you aren't asking selenium question . are you ? You are asking question related to java – Ravi Oct 08 '17 at 09:32
  • I know, But your question is regarding execution – iamsankalp89 Oct 08 '17 at 09:33
  • File is a class which is used to Implement method of takesScreenshot Interface – iamsankalp89 Oct 08 '17 at 09:34
  • Now, you are coming to the point. So, I'm repeating my question again *what is `TakesScreenshot` and `driver`*. The person, who knows OOPs doesn't mean he/she knew selenium also. am I right ? But, other way it is possible – Ravi Oct 08 '17 at 09:39
  • @iamsankalp89 I don't think File class implements TakesScreenshot interface methods. – brij Oct 08 '17 at 09:40
  • getScreenshotAs method which will capture the entire screenshot in form of file. simple. File is a class – iamsankalp89 Oct 08 '17 at 09:42
  • I really don't understand, why he is hiding his code. It's impossible to tell what he is asking without seeing the code. This is my last comment. All the best to OP for getting the answer – Ravi Oct 08 '17 at 09:47
  • It is not be a clear question – iamsankalp89 Oct 08 '17 at 09:47
  • not hiding any code..not sure why the question is not clear..if you want some more info let me know I ll add it in the question. – brij Oct 08 '17 at 10:17

3 Answers3

2

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);
Guy
  • 46,488
  • 10
  • 44
  • 88
1

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

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
0

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
Dmitry Smorzhok
  • 635
  • 11
  • 21
  • thanks for the explanation...but if the implementation of getScreenshotAs method is inside my main driver class, then what is the need to typecasting ? – brij Oct 08 '17 at 09:58
  • casting is needed if the type of `driver ` differ from `TakesScreenshot ` interface and it's implementations (as `Object o` in my example). If in your code `driver` variable is either `TakesScreenshot driver` or any implementation class of `TakesScreenshot` (e.g. `ChromeDriver driver`) you do not need casting and can call method `getScreenshotAs ` directly on `driver` variable. – Dmitry Smorzhok Oct 08 '17 at 10:02
  • So, essentially you are saying that if my driver is an instance of ChromeDriver then I can directly write driver.getScreenshotAs, is that correct ? – brij Oct 08 '17 at 10:05
  • got it..if that is the case not sure why people typecast driver to TakesScreenshot first before taking screenshots in selenium..any guesses on it ? – brij Oct 08 '17 at 10:09
  • I've added an Edit block to my answer (after clarifications in your question I haven't seen before) – Dmitry Smorzhok Oct 08 '17 at 10:17
  • so that means we are going from :WebDriver driver = new ChromDriver() to TakesScreenshot driver = new ChromeDriver()..is it correct ? – brij Oct 08 '17 at 10:19