Friday 5 September 2014

Adding iPhone/iWebDriver node to Selenium Hub


We added nodes for desktop OS and browser combinations. There might be a need to test the application on mobile platforms such as Apple iOS and Android. In this recipe, let's add a node for the iOS Safari browser running on an Apple iPad device.

Let's start this by creating a node for the iOS platform (iPhone or iPad). We can simply add an iOS device or simulator (iPhone or iPad) to the Selenium Grid server by installing the iWebDriver application.

In this recipe, we will add an iPad to the Hub set up in the first recipe.

Getting ready

We need to install the iWebDriver application on the iOS device or simulator. See Chapter 7, Testing on Mobile Browsers, for the steps to install the iWebDriver application on an iOS device or simulator.

How to do it...

Setting up an iOS device or a simulator on a Hub is much easier compared to setting up other types of nodes:

    Close the iWebDriver application if it's running.

    Go to the iOS device or simulator Settings from the Home Screen.

    Select iWebDriver. The settings for iWebDriver will be displayed on the screen.

    In the Grid section, enter the Host and Port details of the Hub we already set in the first recipe, as shown in the following screenshot:

5. Start the iWebDriver application.

How it works...

Setting up an iOS device on the simulator on Hub does not require any configuration file or command-line options. It can be set very easily from the settings UI in iOS.

When we enter the Host and Port details for Hub and start the iWebDriver application, it automatically registers itself on the Hub providing the capability to run tests on the iOS Safari browser.

You can navigate to Hub console to check whether iOS device or simulator is added as a node. In the following screenshot, iPad is added to the list of available nodes in the Grid console:

The Setting up the iWebDriver App for the iPhone/iPad simulator recipe in Chapter 7, Testing on Mobile Browsers

The Setting up the iWebDriver App for an iPhone/iPad device recipe in Chapter 7, Testing on Mobile Browsers

  to Selenium Hub

Now let's also add an Android device or AVD to the Hub for testing applications on the Android browser.

For adding an Android node to the Hub, we need to install a utility written in Python, which will help us to register the Android node to the Hub.

In this recipe, we will add an Android emulator to the Selenium Hub.

Getting ready

We need to install and set up WebDriver APK on an Android device or emulator. See Chapter 7, Testing on Mobile Browsers, for the steps to install and set up WebDriver APK on an Android device or emulator.

This recipe will also need Python installed on the machine.

 12

Distributed Testing with Selenium Grid

How to do it...

To register the Android node on the Hub, we need to install the flynnid ( https://github.  com/davehunt/flynnid) utility in Python.

    Use the following command to install flynnid in Python: pip install flynnid

    Now, let's register the Android node by calling flynnid from the command prompt with the following options:

flynnid --nodeport=8080 --browsername=android --browserver=2.20 --platform=ANDROID

How it works...

The flynnid utility registers the Android node by using configurations provided as command arguments. We need to supply the port of the Android node, browser name, platform Android and version of the Android Server APK installed on the Android device or emulator.

Once the node is registered successfully on the Hub, the Android node will be listed on the Grid console with a tiny Android icon, shown as follows:

13

Distributed Testing with Selenium Grid

See also

ff

ff

The Setting up the Android emulator for Selenium recipe in Chapter 7, Testing on Mobile Browsers

The Setting up the Android device for Selenium recipe in Chapter 7, Testing on Mobile Browsers

Creating and executing Selenium script in parallel with TestNG

Now that we have our Selenium Grid Hub and nodes ready and configured from previous recipes, it's time to run tests in parallel with this distributed environment.

For running tests in parallel, we need a test framework that supports distributing tests on these nodes. We will use TestNG for running tests in parallel for Selenium tests developed with Java bindings.

TestNG (http://testng.org) is another popular Unit testing framework used by the Java community after JUnit. TestNG allows us to create tests that can be executed in parallel. We can set up a group of tests or test suites and have parameters configured to run these tests in parallel. It uses a multithreading model to run tests in parallel.

TestNG requires that we have an XML configuration file with details of configurations that are needed to run the tests in parallel. Before starting the run, TestNG reads this configuration file to setup the test.

In this recipe, we use TestNG to create tests that are parameterized for parallel runs. This recipe is divided in two parts; in the first part we will create a configuration file based on the Hub and the nodes that we have configured so far and then in the second part we will be creating a test case using TestNG annotations and parameterization features.

Getting ready

Make sure you have TestNG installed on the machine from where the tests will be launched.

You can download TestNG from  http://testng.org/doc/index.html.

How to do it...

Let's create and execute a test in parallel with the following steps:

    Before we create a test for parallel execution, first we need to create a configuration file, which TestNG will need to run the tests in parallel. We will provide four parameters for tests, platform, browser, version, and URL.


Distributed Testing with Selenium Grid

Create a new conf.xml file in your projects source directory and add the following code to this file:

<!DOCTYPE suite SYSTEM  http://testng.org/testng-1.0.dtd> <suite name="Parallel Tests" verbose="1" thread-count="4" parallel="tests">

</suite>

2.     Add the <test> node for a test that will be executed on the Windows and Internet Explorer combination in the conf.xml file as follows:

<tests>

<test name="Windows+IE9 Test">

<parameters>

<parameter name="platform" value="Windows" /> <parameter name="browser" value="Internet

Explorer" />

<parameter name="version" value="8" /> <parameter name="url" value="http:// dl.dropbox.com/u/55228056/bmicalculator.html"/>

</parameters>

<classes>

<class name="SeGridTest" /> </classes>

</test>

</tests>

3. Add the following combinations to conf.xml. It should have the following entries:

<suite name="Parallel Tests" verbose="1" thread-count="4" parallel="tests">

<tests>

<test name="Windows+IE9 Test"> <parameters>

<parameter name="platform" value="Windows" />

<parameter name="browser" value="Internet Explorer" />

<parameter name="version" value="8" /> <parameter name="url" value="http://

dl.dropbox.com/u/55228056/

bmicalculator.html"/>

</parameters>

<classes>

<class name="SeGridTest" /> </classes>

</test>

<test name="Mac+Firefox Test">


Distributed Testing with Selenium Grid

<parameters>

<parameter name="platform" value="Mac" />

<parameter name="browser" value="Firefox" />

<parameter name="version" value="9.0.1" />

<parameter name="url" value="http:// dl.dropbox.com/u/55228056/ bmicalculator.html"/>

</parameters>

<classes>

<class name="SeGridTest" /> </classes>

</test>

<test name="iOS Test"> <parameters>

<parameter name="platform" value="MAC" />

<parameter name="browser" value="iPad" />

<parameter name="version" value="" /> <parameter name="url" value="http://

dl.dropbox.com/u/55228056/

mobilebmicalculator.html"/>

</parameters>

<classes>

<class name="SeGridTest" /> </classes>

</test>

<test name="Andorid Test"> <parameters>

<parameter name="platform" value="Android" />

<parameter name="browser" value="Android" />

<parameter name="version" value="2.6" /> <parameter name="url" value="http://

dl.dropbox.com/u/55228056/

mobilebmicalculator.html"/>

</parameters>

<classes>

<class name="SeGridTest" /> </classes>

</test>

</tests>

</suite>


Distributed Testing with Selenium Grid

4.     Now we need to create a test using TestNG. We will create a parameterized test using TestNG's parameterization features. We will create a test for the BMI calculator

application. We will use both the normal web and mobile web version of this application.

Create a new test with the name as SeGridTest and copy the following code:

import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.By;

import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver;

import static org.testng.Assert.*; import org.testng.annotations.AfterTest; import org.testng.annotations.Parameters; import org.testng.annotations.Test; import org.testng.annotations.BeforeTest;

import java.net.MalformedURLException; import java.net.URL;

public class SeGridTest {

WebDriver driver = null;

private StringBuffer verificationErrors = new StringBuffer();

@Parameters({ "platform","browser","version", "url" }) @BeforeTest(alwaysRun=true)

public void setup(String platform, String browser, String version, String url) throws MalformedURLException

{

DesiredCapabilities caps = new DesiredCapabilities();

//Platforms

if(platform.equalsIgnoreCase("Windows"))

caps.setPlatform(org.openqa.selenium.Platform.

WINDOWS);

if(platform.equalsIgnoreCase("MAC"))

caps.setPlatform(org.openqa.selenium.Platform.MAC);

if(platform.equalsIgnoreCase("Andorid"))

17

Distributed Testing with Selenium Grid

caps.setPlatform(org.openqa.selenium.

Platform.ANDROID);

//Browsers

if(browser.equalsIgnoreCase("Internet Explorer")) caps = DesiredCapabilities.internetExplorer();

if(browser.equalsIgnoreCase("Firefox")) caps = DesiredCapabilities.firefox();

if(browser.equalsIgnoreCase("iPad")) caps = DesiredCapabilities.ipad();

if(browser.equalsIgnoreCase("Android")) caps = DesiredCapabilities.android();

//Version

caps.setVersion(version);

driver = new RemoteWebDriver(new URL("http:// localhost:4444/wd/hub"), caps);

// Open the BMI Calculator Application driver.get(url);

}

@Test(description="Test Bmi Calculator")

public void testBmiCalculator() throws InterruptedException {

WebElement height = driver.findElement(By. name("heightCMS"));

height.sendKeys("181");

WebElement weight = driver.findElement(By. name("weightKg"));

weight.sendKeys("80");

WebElement calculateButton = driver.findElement(By. id("Calculate"));

calculateButton.click();

try {

 18

Distributed Testing with Selenium Grid

WebElement bmi = driver.findElement(By. name("bmi"));

assertEquals(bmi.getAttribute("value"),"24.4");

WebElement bmi_category = driver.findElement(By.name("bmi_category"));

assertEquals(bmi_category.getAttribute ("value"),"Normal");

} catch (Error e) {

//Capture and append Exceptions/Errors verificationErrors.append(e.toString());

}

}

@AfterTest

public void afterTest() { //Close the browser driver.quit();

String verificationErrorString = verificationErrors.toString();

if (!"".equals(verificationErrorString)) { fail(verificationErrorString);

}

}

}

How it works...

First, we created a test configuration file listing the combination of tests that we want to run in parallel. Each instance of a test is listed in the <test> tag:

<test name="Windows+IE9 Test"> <parameters>

<parameter name="platform" value="Windows" /> <parameter name="browser" value="Internet Explorer" /> <parameter name="version" value="8" />

<parameter name="url" value="http://dl.dropbox. com/u/55228056/bmicalculator.html"/>

</parameters>

<classes>

19

Distributed Testing with Selenium Grid

<class name="SeGridTest" /> </classes>

</test>

In the <parameters> section we provided platform, browser, and browser version. Last we added URL as we want to run this test both for normal and mobile web versions of the application. Next, we need to specify the name of the test class in the <class> node. If you are storing the test class in a package, then specify the complete path. For example, if we keep SeGridTest in the package com.bmicalcapp.test then the following will be the value that we will pass:

<classes>

<class name="com.bmicalcapp.test.SeGridTest" /> </classes>

Next, we created a parameterized test in TestNG. We added a setup() method, which accepts arguments to set the options for the DesiredCapabilities class for running tests for each combination specified in the configuration file:

@Parameters({ "platform","browser","version", "url" }) @BeforeTest(alwaysRun=true)

public void setup(String platform, String browser, String version, String url) throws MalformedURLException

{

DesiredCapabilities caps = new DesiredCapabilities();

//Platforms

if(platform.equalsIgnoreCase("Windows"))

caps.setPlatform(org.openqa.selenium.Platform.WINDOWS);

if(platform.equalsIgnoreCase("MAC"))

caps.setPlatform(org.openqa.selenium.Platform.MAC);

if(platform.equalsIgnoreCase("Andorid"))

caps.setPlatform(org.openqa.selenium.Platform.

ANDROID);

//Browsers

if(browser.equalsIgnoreCase("Internet Explorer")) caps = DesiredCapabilities.internetExplorer();

if(browser.equalsIgnoreCase("Firefox")) caps = DesiredCapabilities.firefox();

if(browser.equalsIgnoreCase("iPad"))

 20

caps = DesiredCapabilities.ipad();

if(browser.equalsIgnoreCase("Android")) caps = DesiredCapabilities.android();

//Version

caps.setVersion(version);

driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/ hub"), caps);

// Open the BMI Calculator Application driver.get(url);

}

When we execute the tests, we need to tell TestNG to use the conf.xml file for test configurations. TestNG will read this file and create a pool of threads (for this example, it will create four threads) for each combination calling the test class. It will then create and assign an instance of test class to each thread by passing the parameters from the configuration file to the setup() method. TestNG will run the test concurrently for each combination, monitoring and reporting the test status and final results. The following is a report it generates after running the test for combinations specified in the configuration file:

See also

ff   The Creating a data-driven test using TestNG recipe in Chapter 4, Data-driven Testing

21

Distributed Testing with Selenium Grid

Creating and executing Selenium script in parallel with Python

As we have seen in the previous recipe, TestNG provides a very powerful execution framework for running tests in parallel for Java bindings.

However, if you are using Python or another binding to develop Selenium WebDriver tests, you can also run tests in parallel for distributed testing.

In this recipe, we will create and execute tests in parallel with Python bindings using the subprocess module. We will use the Hub and nodes configured in earlier recipes to run these tests.

How to do it...

We will create two test scripts for testing the application with Firefox and Internet Explorer using the following steps. You can also create a single test and parameterize it similar to what we did for TestNG:

1.     For the first Python test, which will test the application functionality using the Firefox browser, name this test as testOnFirefox.py and copy the following code:

from selenium import webdriver

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

from selenium.webdriver.support.ui import WebDriverWait import time, unittest

class OnFirefox (unittest.TestCase): def setUp(self) :

self.driver = webdriver.Remote( command_executor='http://localhost:4444/wd/hub', desired_capabilities=DesiredCapabilities.Firefox)

def test_Google_Search_FF(self): driver = self.driver

driver.get("http://www.google.com") inputElement = driver.find_element_by_name("q") inputElement.send_keys("Cheese!") inputElement.submit()

WebDriverWait(driver, 20).until(lambda driver : driver.title.lower().startswith("cheese!"))

 22

Distributed Testing with Selenium Grid

self.assertEqual("cheese! - Google Search",driver. title)

def tearDown(self): self.driver.quit()

if __name__ == "__main__": unittest.main()

2.     For the second Python test, which will test the application functionality using the Internet Explorer browser, name this test as testOnIE.py and copy the following code:

from selenium import webdriver

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

from selenium.webdriver.support.ui import WebDriverWait import time, unittest

class OnInternetExplorer (unittest.TestCase): def setUp(self) :

self.driver = webdriver.Remote( command_executor='http://localhost:4444/wd/hub', desired_capabilities=DesiredCapabilities.

INTERNETEXPLORER)

def test_Google_Search_IE(self): driver = self.driver

driver.get("http://www.google.com") inputElement = driver.find_element_by_name("q") inputElement.send_keys("Cheese!") inputElement.submit()

WebDriverWait(driver, 20).until(lambda driver : driver.title.lower().startswith("cheese!"))

self.assertEqual("cheese! - Google Search",driver. title)

def tearDown(self): self.driver.quit()

if __name__ == "__main__": unittest.main()

23

Distributed Testing with Selenium Grid

3.     Finally, we need to create a Python script, which will use the subprocess module to run these tests concurrently on different nodes. Name this script as runner.py:

from subprocess import Popen import glob

tests = glob.glob('test*.py') processes = []

for test in tests:

processes.append(Popen('python %s' % test, shell=True))

for process in processes: process.wait()

How it works...

We need to place all three scripts in the same directory. When we execute runner.py, it collects all the tests with names starting as test using the glob function. Then we create a hash for processes and append each test using the Popen function from the subprocess module. The Popen() function calls each test using Python as a subprocess of the main process and waits for the script to complete.

There's more...

We can also use nose module for Python to run the tests in parallel. We need to install nose by using the following command:

easy_install nose==0.11 multiprocessing

After nose is installed, you need to open the folder where all the tests are stored and use the following command:

nosetests --processes=2

This will call the nosetests scripts, which will locate all the files with test prefix in the current directory and start running tests concurrently. In this example, we are running two tests so we need to specify the value for processes argumented as 2. nose module internally uses multiprocessing module for concurrent execution.

See also

ff   The Creating and executing Selenium script in parallel with TestNG recipe

 24

Distributed Testing with Selenium Grid

Setting up Selenium with Jenkins CI for parallel execution

As continuous integration is a widely accepted practice, it is essential that we run Selenium WebDriver tests as part of the CI process for early feedback and increased confidence. Jenkins is widely used as a continuous integration server tool in Java world.

Instead of having a plain Selenium Grid, Jenkins provides a Selenium Grid that can be very well integrated with the CI infrastructure. We can use Jenkins' powerful distributed model for CI to run our Selenium tests in parallel on a Jenkins cluster. In this recipe, we will set up Jenkins for Selenium Grid.

Getting ready

Download and install Jenkins from  https://jenkins-ci.org/.

How to do it...

We need to install Jenkins Selenium Plugin to add Selenium Grid support in Jenkins. Use the following steps to install and configure Jenkins Selenium Plugin:

    Click Manage Jenkins on the Jenkins Dashboard.

    Click on Manager Plugins from the Manage Jenkins option.

    Click on the Available tab.

    Locate and select Jenkins Selenium Plugin from the list of available plugins.

    Click on the Download now and install after restart button.

    A new screen will be displayed Installing Plugins/Upgrades.

7.     After the plugin is downloaded, restart Jenkins. Make sure that no jobs are running while you restart.

8.     After Jenkins restarts, a Selenium Grid link will appear on the left side navigation pane.

    Click on Selenium Grid, the Registered Remote Controls page will be displayed.

    Selenium Grid is now available on  http://localhost:4444/wd/hub for tests.

How it works...

On Jenkins master, Selenium Grid Hub is started on port 4444. This is where all Selenium WebDriver tests should connect, same as using a normal Selenium Grid.

25

Distributed Testing with Selenium Grid

When we set up a Jenkins slave, necessary binaries are copied and Selenium RCs are started automatically by Jenkins. Selenium running on slaves and the Selenium Grid Hub on master are hooked up together automatically.

Along with the standard platform matching capability offered out of the box by Selenium

Grid, Jenkins allows us to specify jenkins.label as a capability, whose value is a Boolean expression of labels/slave names to narrow down where to run the tests:

DesiredCapabilities cap = DesiredCapabilities.firefox(); cap.setCapability("jenkins.label","redhat5 && amd64"); WebDriver driver = new RemoteWebDriver(new URL("http://192.168.1.100:4444/wd/hub"), cap);

In the previous example, Jenkins Selenium Grid will point the tests to the Jenkins slave with redhat5 and amd64 as a label set in Jenkins cluster.

This capability is automatically added to grid nodes that are launched by Jenkins.

Jenkins Selenium Grid can also accept additional nodes running on a Selenium Standalone

Server launched outside of Jenkins.

No comments:

Post a Comment