Google Analytics

Search

To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g.

site:darrellgrainger.blogspot.com CSS selectors

will search for "CSS selectors" but only on my site.


Wednesday, November 30, 2011

Interface versus implementation

I've seen a few people posting Java code for Selenium automation where they have things like:

FirefoxDriver driver = new FirefoxDriver();

If you look at the code for Selenium you will see they have an interface called WebDriver. FirefoxDriver, InternetExplorerDriver, HtmlUnitDriver, RemoteWebDriver all implement WebDriver.

If I'm writing a test suite, I ultimately want to run the suite on Firefox, Internet Explorer, Chrome, etc. but if I implement my framework using FirefoxDriver, I have to edit the code to make it work on InternetExplorerDriver. The proper solution is to write your code using:

WebDriver driver = new FirefoxDriver();

You might argue that I need to change my code still. Okay, so you would implement it using:

WebDriver driver;
    if(browserType.equals("firefox")) {
        driver = new FirefoxDriver();
    } else if(browserType.equals("ie")) {
        driver = new InternetExplorerDriver();
    } else {
        driver = new RemoteWebDriver();
    }

Now all subsequent code will use driver as if it was merely a WebDriver. However, if you look at RemoteWebDriver you will see that it also implements JavascriptExecutor. So if I wanted to use:

driver.executeScript(script, args);

I'll get an error because WebDriver does not support executeScript. My driver is REALLY a RemoteWebDriver and does support executeScript. So how do I access that functionality? Quite simple:

((RemoteWebDriver)driver).executeScript(script, args);


Even better would be:

if(driver instanceof RemoteWebDriver) {
        ((RemoteWebDriver)driver).executeScript(script, args);
    } else if(driver instanceof FirefoxDriver) {
        ((FirefoxDriver)driver).executeScript(script, args);
    } else if(driver instanceof InternetExplorerDriver) {
        ((InternetExplorerDriver)driver).executeScript(script, args);
    }


Now if you run the test suite against a browser that does not support a particular feature, it will skip that part of the code automatically.

And that is proper use if polymorphism.

2 comments:

man9ar00 said...

Helpful post. Regarding your statement:


"The proper solution is to write your code using:

WebDriver driver = new FirefoxDriver();"

Do you mean to same coding and use that but recast to other drivers as needed later where you execute actions like execute javascript where you do

(RemoteWebDriver) driver

And how does this translate to other language bindings like Python, Ruby, or those languages that implement JSONWireProtocol (e.g. PHP, Perl)?

Is there some methodology we can follow that is common across all languages?

Darrell said...

Hi man9ar00. Yes, you want to declare variables as the interface. If the implementation extends more than the interface you cast the variable to the actual type of the object.

This is proper technique for object oriented programming.

I have done some Python programming and although it has classes, it is quite a different language from Java. For example, if a class in Java does not full and correct implement an interface the code won't compile. In Python this is not true. If it seems close and all the bits you are going to use are there, it is fine. Essentially, people don't select Python for a strictly object oriented programming language.

I have not programmed Ruby so I cannot say whether or not this can be translated to Ruby.