WebDriver driver = new FirefoxDriver(); How does it work?

WebDriver driver = new FirefoxDriver();

First line you wrote in test automation.

A line used in all your test automation scripts.

So common and simple.

How does it work?

Why does it need both WebDriver and FirefoxDriver for creating the browser driver object?

 

Lets start.

Instead of talking about theory, we will use a concrete example.

The next test script uses only WebDriver for creating the driver object.

public class TestClass
{
String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";

@Test
public void testSearch() throws Exception
{
WebDriver webDriver = new WebDriver();
webDriver.get(URL);

WebElement searchTextBox = webDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

webDriver.quit();
}
}

The script is very simple:

  1. it creates the driver object
  2. it opens the site url
  3. it finds the search text box element
  4. it clears the text box
  5. it types a value in the text box
  6. it quits the driver object

 

When creating the driver object, only the WebDriver type is used:

WebDriver webDriver = new WebDriver();

And we get an error in Eclipse:

cannot instantiate the type WebDriver

Logically, this makes sense.

Which browser driver are we talking about?

Is it the driver for Firefox, Chrome, Internet Explorer?

 

So, we can replace the WebDriver type with another type, for example, FirefoxDriver.

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";

@Test
public void testSearch() throws Exception
{
FirefoxDriver firefoxDriver = new FirefoxDriver();
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

firefoxDriver.quit();
}
}

No error is generated in this case and the code can be executed.

Since the driver object is both created and quitted in the test script, we can move these things in the test fixtures (setUp() and tearDown() methods annotated with @Before and @After):

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
FirefoxDriver firefoxDriver;

@Before
public void setUp()
{
firefoxDriver = new FirefoxDriver();
}

@After
public void tearDown()
{
firefoxDriver.quit();
}

@Test
public void testSearch() throws Exception
{
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

We could even create a separate method that returns the driver object:

public class TestClass{

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
FirefoxDriver firefoxDriver;

@Before
public void setUp()
{
firefoxDriver = getFirefoxDriver();
}

@After
public void tearDown()
{
firefoxDriver.quit();
}

@Test
public void testSearch() throws Exception
{
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

private FirefoxDriver getFirefoxDriver()
{
return new FirefoxDriver();
}

}

Things work pretty well in the present script.

But lets say that we want to execute the script in Chrome.

We will need to make a few changes to the test class:

  • the FirefoxDriver field will use the ChromeDriver type
  • a new getChromeDriver() method is needed
  • the getChromeDriver) method returns a ChromeDriver object

 

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
ChromeDriver chromeDriver;

@Before
public void setUp()
{
chromeDriver = getChromeDriver();
}

@After
public void tearDown()
{
chromeDriver.quit();
}

@Test
public void testSearch() throws Exception
{
chromeDriver.get(URL);

WebElement searchTextBox = chromeDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

private ChromeDriver getChromeDriver()
{
System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe");
return new ChromeDriver();
}

}

This script works as well but things are getting complicated.

How can we modify the script to work for both browsers instead of having a script for each browser?

We can combine the getFirefoxBrowser() and getChromeBrowser() methods into a single method.

private ????? getDriver(String browserName) throws Exception
{
if (browserName.equalsIgnoreCase("chrome"))
return getChromeDriver();

if (browserName.equalsIgnoreCase("firefox"))
return getFirefoxDriver();

throw new Exception("invalid browser name");
}

The method has a parameter for the browserName and returns either a ChromeDriver or a FirefoxDriver object.

Obviously, this is a problem as we cannot have 2 different types as the method return type.

The method cannot return FirefoxDriver some times and ChromeDriver other times.

To solve this, we need a type that can be used instead of both FirefoxDriver and ChromeDriver.

 

And, you guesses, this is WebDriver.

Why can WebDriver be used instead of FirefoxDriver and ChromeDriver?

Because, in comparison with FirefoxDriver() and ChromeDriver() which are classes (so objects can be created for them), WebDriver is an interface.

 

An interface is just a template that is implemented by a class.

It specifies what fields and methods the class should have but without providing more details.

The classes that implement the interface will provide the methods implementations.

So WebDriver is an interface and FirefoxDriver and ChromeDriver classes that implement the WebDriver interface.

See below a part of the WebDriver interface (from selenium-java-2.52.0-srcs.jar file):

 
public interface WebDriver extends SearchContext {

void get(String url);
String getCurrentUrl();
String getTitle();
List<WebElement> findElements(By by);
WebElement findElement(By by);
String getPageSource();
void close();
void quit();

.........................................

.........................................

}

Just method declarations and no method implementations.

What about FirefoxDriver()?

FirefoxDriver() is not really implementing the WebDriver interface.

It is inheriting instead from the RemoteDriver class which implements the WebDriver interface.

This is some content of the RemoteDriver class:

public class RemoteWebDriver implements WebDriver, JavascriptExecutor,FindsById, FindsByClassName, FindsByLinkText, FindsByName, FindsByCssSelector, FindsByTagName, FindsByXPath,
HasInputDevices, HasCapabilities, TakesScreenshot {

private static final Logger logger = Logger.getLogger(RemoteWebDriver.class.getName());
private Level level = Level.FINE;

private ErrorHandler errorHandler = new ErrorHandler();
private CommandExecutor executor;
private Capabilities capabilities;
private SessionId sessionId;
private FileDetector fileDetector = new UselessFileDetector();
private ExecuteMethod executeMethod;

public void get(String url) {
execute(DriverCommand.GET, ImmutableMap.of("url", url));
}

public String getTitle() {
Response response = execute(DriverCommand.GET_TITLE);
Object value = response.getValue();
return value == null ? "" : value.toString();
}

public String getCurrentUrl() {
Response response = execute(DriverCommand.GET_CURRENT_URL);
if (response == null || response.getValue() == null) {
throw new WebDriverException("Remote browser did not respond to getCurrentUrl");
} else {
return response.getValue().toString();
}
}

public List<WebElement> findElements(By by) {
return by.findElements(this);
}

public WebElement findElement(By by) {
return by.findElement(this);
}

protected WebElement findElement(String by, String using) {
if (using == null) {
throw new IllegalArgumentException("Cannot find elements when the selector is null.");
}

Response response = execute(DriverCommand.FIND_ELEMENT,
ImmutableMap.of("using", by, "value", using));
Object value = response.getValue();
WebElement element;
try {
element = (WebElement) value;
} catch (ClassCastException ex) {
throw new WebDriverException("Returned value cannot be converted to WebElement: " + value, ex);
}
setFoundBy(this, element, by, using);
return element;
}

Lots of method implementations in this case.

That is why, in the first code example, we could not create the WebDriver object. Because WebDriver is an interface and not a class.

It is not possible to create an object for an interface and instantiate it using the interface type.

Interfaces have a very interesting characteristic, though.

It is possible to create an object for an interface and instantiate it using any of the classes that implements the interface.

For example,

WebDriver driver = new FirefoxDriver();

or

WebDriver driver = new ChromeDriver();

Looks familiar?

Having clarified the difference between WebDriver, FirefoxDriver and ChromeDriver, we can finalize the sample project code:

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
WebDriver driver;

@Before
public void setUp() throws Exception
{
driver = getDriver("firefox"); 
//or driver = getDriver("chrome");
}

@After
public void tearDown()
{
driver.quit();
}

@Test
public void testSearch() throws Exception
{
driver.get(URL);

WebElement searchTextBox = driver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);
}

private ChromeDriver getChromeDriver()
{
String chromeDriverPath = 
"C:\\Selenium\\BrowserDrivers\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", chromeDriverPath);
return new ChromeDriver();
}

private FirefoxDriver getFirefoxDriver()
{
return new FirefoxDriver();
}

private WebDriver getDriver(String browserName) throws Exception
{
if (browserName.equalsIgnoreCase("chrome"))
return getChromeDriver();

if (browserName.equalsIgnoreCase("firefox"))
return getFirefoxDriver();

throw new Exception("invalid browser name");
}

}

The driver class field uses the WebDriver interface for its type.

The getDriver() method returns a WebDriver object that is initialized with either a FirefoxDriver object or with a ChromeDriver object.

Thats why all these types are needed.

So that your scripts are flexible and can use any WebDriver object is required.

 

 

5 Comments