Test Automation with Selenium WebDriver and Selenium Grid – part 2: Creating and Executing Tests

In part 1 in the series (read it here) I presented Selenium, a widely known tool for browser test automation.

Starting with Selenium 2, the most important components from the suite are Selenium WebDriver and Selenium Grid. In part 1 I showed how easy is to setup a testing grid with multiple OS and browsers. Now in part 2 I will show how to leverage WebDriver API to create and execute tests.

A Brief History of WebDriver

WebDriver was born under Google sponsorship around 2009 as a totally separated framework from what it was Selenium at the time. WebDriver’s raison d’être was to prevent some issues that Selenium 1 and other browser test automation tools had, because of their JavaScript based approach. The sandbox model of JavaScript engines usually limits what stuff can be automated from a script, just to secure the browser and the underlying OS from malicious scripts.

WebDriver is based on different ‘drivers’, understood as components to interact as close as possible with each different browser, may it be an automation interface or a browser extension.

This approach allows better interaction with the browser, closer to how a human being would interact with it. For example, instead of changing the text on a field with JavaScript, the driver is capable of simulating key press events, exactly as it would be if a human was typing the text in the field.

In addition to that, WebDriver API is probably cleaner, simpler to use.

With all of that in mind, both communities decided to merge efforts.

Preparing the Environment

Our playground will consist of a simple web application built with JSP, JSTL, Spring Framework/MVC and Hibernate. The IDE in use is Eclipse Indigo SR1 with SpringSource plug-ins and other stuff installed. Apache Maven 2 is used for build automation and dependency management.

If using Maven, preparation is immediate, just add the dependency with selenium-java artifact:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>2.16.1</version>
    <scope>test</scope>
</dependency>

This artifact contains the WebDriver API for Java, along with Selenium 1 API for backwards compatibility. This API exists for other languages as C#, Ruby or Python as well.

Configuring Test Parameters

Each test will follow a similar process: connect to the hub, tell it where the target application is, what browser to use and the actual test commands. As a best practice I recommend that both the hub connection information and the target application location are configurable and externally provided. If not, the test code or test configuration (i.e. a properties file) should be updated if the hub or the test server changes. Moreover, it is critical for a successful deployment of a test suite across the team and continuous integration to provide these settings externally.

A pattern suggested is to provide both settings as environment variables or system properties. Code could be like this:

public class CodesIntegrationTestCase {
    private static final Logger logger =
        LoggerFactory.getLogger(CodesIntegrationTestCase.class);
    private static String SELENIUM_HUB_URL;
    private static String TARGET_SERVER_URL;

    @BeforeClass
    public static void initEnvironment() {
        SELENIUM_HUB_URL = getConfigurationProperty(
           "SELENIUM_HUB_URL",
           "test.selenium.hub.url",
           "http://localhost:4444/wd/hub");
        logger.info("using Selenium hub at: " + SELENIUM_HUB_URL);

        TARGET_SERVER_URL = getConfigurationProperty(
            "TARGET_SERVER_URL",
            "test.target.server.url",
            "http://localhost:8080/sdc.samples.selenium");
        logger.info("using target server at: " + TARGET_SERVER_URL);
    }

    private static String getConfigurationProperty(
            String envKey, String sysKey, String defValue) {
        String retValue = defValue;
        String envValue = System.getenv(envKey);
        String sysValue = System.getProperty(sysKey);
        // system property prevails over environment variable
        if (sysValue != null) {
            retValue = sysValue;
        } else if (envValue != null) {
            retValue = envValue;
        }
        return retValue;
    }

Configuring the Browsers

Next step to write the tests is to decide in which browsers each test will run. Election can be as detailed as needed. You can just decide on the browser family or be precise and request and specific browser version in a certain operating system. Either the case, when the test reaches the hub, it will find an available node with the requested capabilities and delegate the test execution to it.

To do that, just leverage the org.openqa.selenium.remote.DesiredCapabilities class or even better its factory methods.

If a test would need to be executed in a Mozilla Firefox browser, just use this statement:

DesiredCapabilities browser = DesiredCapabilities.firefox();

If you need to test inside a Microsoft Internet Explorer browser:

DesiredCapabilities browser = DesiredCapabilities.internetExplorer();

In the case you need more control, you can be more specific. For example to request an Opera 11 browser in a Linux OS, you could use:

DesiredCapabilities browser = DesiredCapabilities.opera();
browser.setVersion("11");
browser.setPlatform(Platform.LINUX);

Reusing Test Code for Cross-Browser Compatibility Tests

In many situations it is desirable that the same test is executed in different browsers and OS mixes. If that is the case, it is a best practice to reuse the test code and provide the browser as a parameter. It is also a best practice to use different test methods for each browser. Thus, in case of test error or failure, you will have immediate feedback on which is browser where the test failed.

A suggested code pattern could be like this:

    @Test
    public void testIE()
            throws MalformedURLException, IOException {
        DesiredCapabilities browser =
            DesiredCapabilities.internetExplorer();
        testCodesCrud(browser);
    }

    @Test
    public void testFirefox()
            throws MalformedURLException, IOException {
        DesiredCapabilities browser =
            DesiredCapabilities.firefox();
        testCodesCrud(browser);
    }

    @Test
    public void testChrome()
            throws MalformedURLException, IOException {
        DesiredCapabilities browser =
            DesiredCapabilities.chrome();
        testCodesCrud(browser);
    }

    @Test
    public void testOpera()
            throws MalformedURLException, IOException {
        DesiredCapabilities browser =
            DesiredCapabilities.opera();
        testCodesCrud(browser);
    }

The four test methods, one for a different browser family, is effectively reusing the same test code inside the testCodesCrud() method. The browser is passed as a parameter to the test.

Contacting the Hub and Initiating the Test

Next step in the test code is to instantiate a WebDriver to contact with the hub, ask for a browser and initiate the test commands by accessing the application under test in the URL where it is available. It is best practice to always close the driver, for example using a finally block to do that. Closing the driver will, among other things, close the browser process in the remote node.

A suggested pattern for writing the test code would be like this:

    public void testCodesCrud(DesiredCapabilities browser)
            throws MalformedURLException, IOException {
        WebDriver driver = null;
        try {
            driver = new RemoteWebDriver(
                new URL(SELENIUM_HUB_URL), browser);

            // test starts in Codes entity list page
            driver.get(TARGET_SERVER_URL + "/CodesView.do");

            // rest of test commands come here
        } finally {
            if (driver != null) {
                driver.quit();
            }
        }
    }

Interacting with the Browser

Although the WebDriver API is rich, most of the times the test code will just find elements and interact with them using simple methods as sendKeys() or click(). Elements on a page can be located by name, id, class, tag or XPath expression.

Let’s imagine that our test would like to verify that the initial listing on the Codes entity contains three values and that those three are “A”, “C” and “D”. The test code would be like this:

    // confirm initial data list
    List<WebElement> codeElems = driver.findElements(By.name("code"));
    List<String> codes = new ArrayList<String>();
    for (WebElement codeElem : codeElems) {
        codes.add(codeElem.getAttribute("value"));
    }

    assertEquals(3, codes.size());
    assertTrue(codes.contains("A"));
    assertTrue(codes.contains("C"));
    assertTrue(codes.contains("D"));

To interact with form controls, as buttons, the process is analogous: find the element and issue a simple command to it:

    // get add button
    WebElement addForm = driver.findElement(By.name("add"));
    WebElement addButton = addForm.findElement(By.name("add"));

    // submit to add a new record
    addButton.click();

It is a best practice to add some delay time when loading a new page. With this approach, the test is protected from failing if the new page does not load immediately while ensuring that the right page is loaded in a timely manner using a time out:

    // wait up to 10 seconds for the Codes detail page to load
    (new WebDriverWait(driver, 10)).until(
        new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver d) {
                // the expected condition in this case
                // is the new page title 
                return d.getTitle().equals("Codes Detail Page");
            }
        });

Once the detail page is loaded, there is an empty form to add a new Codes entity. Interacting with this form is easy with the WebElement interface:

    WebElement detailForm = driver.findElement(By.name("detail"));
    WebElement codeField = detailForm.findElement(By.name("code"));
    codeField.sendKeys("P");
    WebElement valueField = detailForm.findElement(By.name("value"));
    valueField.sendKeys("postponed");
    // confirm add and return to view page
    WebElement okButton = detailForm.findElement(By.name("ok"));
    okButton.click();

Alternatively, the submit() method from WebElement interface can be used. This method will just submit the form in which the element is located. Thus, the final two statements could be replaced with:

    ...
    valueField.sendKeys("postponed");
    // confirm add and return to view page
    valueField.submit();

For the rest of the test code I just combined what is shown before to verify that new Codes entities can be added, updated and deleted, and each time the listed values are consistent.

Executing the Test

Once the test is finished, deploy the application into a local Tomcat server using Eclipse WTP. Once the application is up, the unit test can be executed as usual. Don’t forget to add the hub and target server URLs as environment variables or system properties to the test launcher configuration:

-Dtest.selenium.hub.url=http://xxxxx:4444/wd/hub
-Dtest.target.server.url=http://xxxxx:8080/sdc.samples.selenium

Once the test is executed, if the node boxes are visible you may see how each browser shows, the application is tested and the browser is closed. Like an automated piano, pages will show, text will be entered and buttons will be pressed, just like if someone were interacting with the mouse and keyboard:

At the end, test results will be shown in JUnit view inside Eclipse IDE:

Recap

In this post I’ve shown how to write and execute browser tests using Selenium WebDriver and Selenium Grid. Test execution is distributed across nodes in the grid, depending on the available browsers while results are collated by the hub process. Test is launched from Eclipse IDE and results are shown in JUnit view, as with any other regular unit test.

In part 3 (read it here), I will show how to execute the Selenium test just created under Continuous Integration using Maven, Cargo and Jenkins and how to get insights on the code coverage for those tests with Sonar and JaCoCo.

Author: deors

senior technology architect in accenture, with a passion for technology related stuff, celtic music and the best sci-fi, among other thousand things!

42 thoughts on “Test Automation with Selenium WebDriver and Selenium Grid – part 2: Creating and Executing Tests”

  1. Here the apprach is discueed on execution for multiple browser on 1 machine/1 os..
    How can we achieve distribution and execution of test cases across different virtual machines for same browser(of different versions) ??

    1. There’s nothing really different to do. If you have VM 1 with IE 6, and VM 2 with IE 7, just register both VMs ensuring you add version information to the node command. Then, on execution, just run the test twice, specifying the desired browser version. Use the setVersion() method for that.

      1. and how can we distribute the cases on the browsers accessed on the VMs simultaneously?

      2. This is done automatically by the hub, depending on which browsers (and versions) are registered (no matter in which local, remote or virtual node they are) and available at a given point of time (i.e. another test suite is being executed and making use of the browsers).

  2. So if I run a test suite of about 90 cases and i register 2 VMs for the same browser and version(i.e. IE9 Win7) and run the command for execution twice,will those 90 test cases be automatically distributed on the 2 VMs?

    1. They should, all the running cases would be distributed across available VMs. Take also in consideration that when you register a browser a maximum number of concurrent sessions is set (5 by default, if I remember right), so potentially you may have multiple tests running simultaneously in the same VM, based on your configuration.

  3. Thank you for your response. I will just what I am trying to acheive and steps I have followed so far :
    am using Selenium webdriver and selenium 2.19 jar to distrivbute the execution of tets cases on multiple machine for a particulra browser.

    1. Launched IE browser on different VMs by listing the VMs as nodes to the Hub at different ports. Have verified the different port connection from Grid Console.
    I have started only one node in the hub in each VM

    java -jar selenium-server-standalone-2.19.0.jar -role webdriver -hub http://localhost:4444/grid/register -port 5557 -browser “browserName=internet explorer,maxSession=5,version=8,maxInstances=1,platform=WINDOWS”
    2. Pointed my local framework to Hub and started the execution through maven command on the local machine which hits the “hub” through the code.
    The changes which I do to point my local to hub is.
    Change in system property :
    serverUrl = http://bur-dtg-sele01.wds.######.com:4444/wd/hub
    //serverUrl = http://localhost:4444/wd/hub
    and I just execute this command from my local
    mvn test -Dtest=TestInternetExplorer -Dscope=FULL -Denvironment=QA
    where
    public class TestInternetExplorer {
    @Test
    public void version8() throws Exception {
    Thread.sleep(Long.parseLong(“500”));
    RunTestScenario scenario = new RunTestScenario();
    System.out.println(“## running Internet Explorer 8 automation”);
    scenario.runScenario(Global.INTERNET_EXPLORER,”8″,”WINDOWS”,”username”);
    }
    @Test
    public void version8new() throws Exception {
    Thread.sleep(Long.parseLong(“1000”));
    RunTestScenario scenario = new RunTestScenario();
    System.out.println(“## running Internet Explorer 8 automation”);
    scenario.runScenario(Global.INTERNET_EXPLORER,”8″,”WINDOWS”,”username2″);
    }
    @Test
    public void version9() throws Exception {
    Thread.sleep(Long.parseLong(“2000”));
    RunTestScenario scenario = new RunTestScenario();
    System.out.println(“## running Internet Explorer 9 automation”);
    scenario.runScenario(Global.INTERNET_EXPLORER,”9″, “WINDOWS”,”username3″);
    }
    @Test
    public void version7() throws Exception {
    Thread.sleep(Long.parseLong(“3000”));
    RunTestScenario scenario = new RunTestScenario();
    System.out.println(“## running Internet Explorer 7 automation”);
    scenario.runScenario(Global.INTERNET_EXPLORER,”7″, “XP”,”username4″);
    }*/
    }

    I set desired capabilties as :
    if (strBrowser.contains(Global.INTERNET_EXPLORER)) {
    capability = DesiredCapabilities.internetExplorer(); capability.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
    }

    Along with this I set version and platform.

    capability.setVersion(strVersion);
    public void setPlatform(String strPlatform) {

    this.strPlatform = strPlatform;

    if (this.strPlatform.compareToIgnoreCase(“WINDOWS”) == 0){
    capability.setCapability(CapabilityType.PLATFORM, Platform.WINDOWS);
    }
    if (this.strPlatform.compareToIgnoreCase(“XP”) == 0){
    capability.setCapability(CapabilityType.PLATFORM, Platform.XP);
    }
    if (this.strPlatform.compareToIgnoreCase(“VISTA”) == 0){
    capability.setCapability(CapabilityType.PLATFORM, Platform.VISTA);
    }
    if (this.strPlatform.compareToIgnoreCase(“MAC”) == 0){
    capability.setCapability(CapabilityType.PLATFORM, Platform.MAC);
    }
    }
    Here in different methods, I explcitly pass versions and platform as parameters. and I expect it to redirect to the respective listener.

    Is it that I just need to use only 1 method in this class to distribute the test cases or should I not pass versions and platform as parameters, because I want the execution to happen in all the machine.
    Say if I have test suite of 100 test cases, I want each VM to execute 25 TCs simultaneoulsy.
    I am not sure if it is the connection issue from my local to the hub.
    FF works well with distributed testing on only 1 VM with multiple browsers.

    I am facing this issues only for IE. So actually my question is, if the code which I have shared has any loophole which I am not able to find out, because somehow it is not connecting to all the listeners at once.

    I have used multithreading in case of parallel browser testing for 1 VM but I am not sure ,is the same multithreading approach will work for multiple VMs as well.
    Please suggest if you have any resolutions. I am not very proficient with Selenium webdriver and dont have much idea on this.

    1. After reading this (I also read your post in Selenium Users list) I suggest trying to simplify the test code as much as possible to discard the problem is there. I mean, work directly with Capabilities interface and check if the error persists. I also suggest trying to simplify hub and nodes configuration, strip browser config string at the minimum (just browser name and version, platform is implicit for IE), and use the “-role hub” and “-role node” switches as shown in these posts. I’ve never have this kind of issue with grid hub not able to detect registered browsers, or to be precise, where I’ve had it is because browser config string and Capabilities info in the test code were not compatible.

    1. Except for some issues with the latest Firefox and Selenium 2.16, no other issues that I can remember – IE 8-9, Chrome 19-21, Opera 11 and Android 3 are all working well. I have pending to check how it goes with Android 4 browser and Opera 12, and re-check with latest Firefox and the latest Selenium.

  4. is it re queried “j son” file for running scripts in web driver using selenium grid ? what is the need of json file ?

      1. Could u please explain about JSON file how could we registering nodes with JSON ?

  5. I got lost in how the tests mentioned above execute in parallel without using something like TestNG. If you can point out what I am missing here, thanks!

    1. Well in this post we’ve not discuss about test execution in parallel. However, now that you mention it, there are two types of parallelisation to consider:
      – Within the context of a build process, that would require TestNG or a custom runner in JUnit
      – Within the context of the Selenium Grid, which applies to multiple build process
      Focusing on the second one, if for example developer A, developer B and CI server are running build processes at the same time, the Grid hub will execute tests in parallel, according to the browsers registered and available. It is important to configure the number of concurrent browsers allowed in each node and fine tune settings depending on the team build needs and resources available.

  6. Thank you for this article it has been very helpful.

    I am a novice programmer and have started just by creating and running my tests locally using Junit. I want to now extend them to various VM’s using RemoteWebDriver. This is my first crack at using Selenium and JUnit so I’m struggling with the best way to design and structure my tests. E.g.

    This is how my tests are each individually structured now in Junit (I will set the Hub and Host/URI parameters in an external config file later for reusability.
    ). Let’s say I also have a LoginFailTest and a LogoutTest in Junit so I can see the individual test results for each test case.

    public class LoginTest{
    private final static WebDriver driver = new FirefoxDriver();
    private final static String hostAndPortAndContext = “http://IP_here”;
    private final static String URI = “/get-started”;

    @Before
    public void before() throws Exception {
    driver.get(hostAndPortAndContext+URI);
    }

    @Test
    public void test() throws Exception{

    GetStartedPage getStartedPage = new GetStartedPage(driver);
    LoginPage loginPage = getStartedPage.LetsGetStarted();

    assertEquals(“Pages Title”, loginPage.getLoginPageWelcomeMessage());
    System.out.println(“Attempting to login with correct credentials…”);
    HomePage homePage=loginPage.loginAs(“e-mail”, “password”);
    assertEquals(“Username: e-mail”, homePage.getLoggedInUserName());
    assertTrue(homePage.hasLogoutOption());
    }

    @After
    public void tearDown() throws Exception {
    driver.manage().deleteAllCookies();
    }

    @AfterClass
    public static void afterItsAllDone() {
    driver.quit();
    }

    }

    So after I’ve configured the Selenium grid I need to setup a class that will run all of my test cases on the various grid nodes Similar to what you have done here. What is the best way for me to repurpose or use these JUnit test classes for use with RemoteWebDriver as you are doing? Is it possible to run multiple JUnit tests from another Junit class? Is it possible to pass a webdriver object from one JUnit test class and have another accept it as an argument?

    @Test
    public void testIE()
    throws MalformedURLException, IOException {
    DesiredCapabilities browser =
    DesiredCapabilities.internetExplorer();
    JUnitCore.runClasses(LoginTest(browser),LoginFailTest(browser),LogoutTest(browser)…);
    }

    @Test
    public void testFirefox()
    throws MalformedURLException, IOException {
    DesiredCapabilities browser =
    DesiredCapabilities.firefox();
    JUnitCore.runClasses(LoginTest(browser),LoginFailTest(browser),LogoutTest(browser)…);
    }

    @Test
    public void testChrome()
    throws MalformedURLException, IOException {
    DesiredCapabilities browser =
    DesiredCapabilities.chrome();
    JUnitCore.runClasses(LoginTest(browser),LoginFailTest(browser),LogoutTest(browser)…); }

    @Test
    public void testOpera()
    throws MalformedURLException, IOException {
    DesiredCapabilities browser =
    DesiredCapabilities.opera();
    JUnitCore.runClasses(LoginTest(browser),LoginFailTest(browser),LogoutTest(browser)…); }

    The more research I do the more questions I have and I don’t feel like I’m getting anywhere. If I need to completely re-structure the way I’m writing my test cases I’d rather do it now before I get too far ahead of myself.

    As you can see I am no master programmer. Any suggestions would be appreciated.

  7. Hi, thanks for your previous article it resolved my issues in launching hub…..it was soo nice….
    & now my problem is to run my test class in multiple browsers at a time…. You told about maven project in this article….I want that for java project I m new to selenium & I don’t know about maven please help me…

    I would be thankful to you……..

  8. ​HI
    I was trying to run the automation scripts using Selenium Grid(Hub & Node ), and i have a scenario of to Press Enter so using KeyBoardUtility senkeys. But this Action of KeyPress is performing in the Hub machine from where we are running the scripts, instead of doing it in Node machine. Do anybody have an idea to overcome this issue.

    1. Hi, I’ve never faced this situation before. I guess that sendkey command is in your script so it should be done on each node as with any other command e.g. send text, right? Have you tried in Selenium forum? Maybe is a known bug.

      1. yeah sendKeys is part of my script , its mainly to handle the POPUP coming in my flow. Is there any location i mean website to post this query.

  9. Thank you doers for your great article. Could you please share above code as a full package?

  10. Hi,
    Very nice article!
    I am using grid but facing an issue. I will appreciate if you can help me in this:
    I am trying to setup 1 hub – multiple nodes (nodes at different machines) configuration.Everything is fine except there is no guarantee on which node particular browser will be launched. I am using selenium 2.32 jar with java. Can I distinguish nodes using some property like node port? I tried setting ‘-port xxxx’ in node cmd but its not useful.
    Let me know if you want any more info.
    Thanks
    Amit

  11. Hi,

    We have secure browswer. How will I test the secure browser with selenium? any advice..I am new in the selenium and I want to go develop Selenium and Java.

    thanks in advance,
    Joy

  12. Hi,

    In our case, we have almost 200 [ independent ] test case to execute on FF which takes 10-12 hours to execute all. How I can use Hub which will run cases in parallel to reduce execution time. With the example discussed above running same test on different Browser.

    Can you suggest me good idea ?

    Thanks,
    Prit

    1. If you have multiple test nodes with Firefox browser installed (and registered, so the Hub knows), you should get your tests run in parallel across nodes in the grid. Be aware that for this to work, tests have to be prepared to run in parallel.

  13. If I want for chrome(/*some condition) different condition for firefox and different condition for IE,
    what is the syntax? I m using NUNIT in C#

    1. If I needed to do that I would have two test methods, one per browser, and I would reuse as much logic as possible with private methods. However I would not recommend to add browser-specific logic or switches inside the test logic itself. Keep that in the public test method to improve reusability and maintainability going forward.

      1. hi , i am new to selenium grid , i have set up the hub and node . But when running the tests , i am getting below error ;

        “Error forwarding a new session cannot find : Capabilities” .

        Kindly reply .
        Thanks
        Raghutej.

      2. Well the error message is not so clear, but it is likely that in your grid there is no compatible browser the tests you are running.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s