Web Driver Sampler

Download

Introduction

Unlike most Samplers, the Web Driver Sampler provides the reader with an empty editor field, much like a blank canvas. Do not be dismayed, as with great responsibility comes great power! This wiki will document what each of these fields does, and will also detail some of the common use cases that the scripter may have. If the reader has not yet done so, it is highly recommended that they quickly go over the 2 minute tutorial before continuing on with this section.

General concepts

When a new Web Driver Sampler is added, the user interface will resemble something as follows:

The main fields that the scripter can make use of are:

  1. Name - for the test that each Web Driver Sampler will have. This is used by the reports.
  2. Parameters - is optional and allows the reader to inject variables used in the script section.
  3. Script - allows the scripter to control the Web Driver and capture times and success/failure outcomes.

Within the Script section, the Sampler automatically injects a WebDriverScriptable object with the name of WDS. This object has the following properties for the scripter to use:

  1. WDS.name - is the value provided in the Name field (above).
  2. WDS.vars - <a href="../api/org/apache/jmeter/threads/JMeterVariables.html">JMeterVariables</a> - e.g.

<code> vars.get("VAR1"); vars.put("VAR2","value"); vars.remove("VAR3"); vars.putObject("OBJ1",new Object()); </code>

  1. WDS.props - JMeterProperties (class <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html"><code>java.util.Properties</code></a>) - e.g.

<code> props.get("START.HMS"); props.put("PROP1","1234"); </code>

  1. WDS.ctx - <a href="../api/org/apache/jmeter/threads/JMeterContext.html">JMeterContext</a>
  2. WDS.parameters - is the value provided in the Parameters field (above).
  3. WDS.args - is an array of the strings provided in the Parameters field, but split by the space ' ' character. This allows the scripter to provide a number of strings as input and access each one by position.
  4. WDS.log - is a Logger instance to allow the scripter to debug their scripts by writing information to the jmeter log file (JMeter provides a GUI for its log entries)
  5. WDS.browser - is the configured Web Driver browser that the scripter can script and control. There is detailed documentation on this object on the Selenium Javadocs page.
  6. WDS.sampleResult - is used to log when the timing should start and end. Furthermore, the scripter can set success/failure state on this object, and this SampleResult is then used by the JMeter reporting suite. The JMeter javadocs provide more information on the API of this object.

Reader's responsibility

As mentioned previously, this sampler provides the scripter with a lot of control. This means that unlike most samplers, the scripter is responsible for performing several tasks within the Script panel:

  1. Capture the sample times by calling the `WDS.sampleResult.sampleStart()` and `WDS.sampleResult.sampleEnd()` methods.
  2. Executing the sampler task on the `WDS.browser` instance.
  3. Verifying if the task completed successfully. By default the sampler assumes success.

The following is a very simple sampler that captures all responsibilities mentioned above.

// 1a. Start capturing the sampler timing
WDS.sampleResult.sampleStart()
// 2. Perform the Sampler task
WDS.browser.get('http://google.com.au')
// 1b. Stop the sampler timing
WDS.sampleResult.sampleEnd()
// 3. Verify the results
if(WDS.browser.getTitle() != 'Google') {
    WDS.sampleResult.setSuccessful(false)
    WDS.sampleResult.setResponseMessage('Expected title to be Google')
}

There is a way to create sub-samples within main sample. To enable that, you need to set JMeter property:

webdriver.sampleresult_class=com.googlecode.jmeter.plugins.webdriver.sampler.SampleResultWithSubs
Then use `WDS.sampleResult.subSampleStart(String label)` and `WDS.sampleResult.subSampleEnd(boolean successful)` to create intermediary measurements in the middle of the process (available only when not overridden by  property).

Script field

As mentioned above, the *WDS* object is automatically available to the scripter to use.  However, since JMeter is running on a JVM, the scripter has access to any class loaded in the environment.  The following is an example of creating a java Date object:
var d = new java.util.Date()

Notice that the variables are declared with the full package and class name. The Rhino environment allows the scripter to provide shortcuts to some frequently used packages and classes. This is well documented on the Rhino website, and the following illustrates an example where a package is represented by a javascript variable:

var support_ui = JavaImporter(org.openqa.selenium.support.ui.WebDriverWait)
var wait = new support_ui.WebDriverWait(WDS.browser, 5000)

Logging

This is useful for the scripter especially when they are attempting to debug their scripts. To see the `jmeter.log` file, refer to the jmeter docs, and the scripter can easily append to this by following the instructions below:

WDS.log.info(WDS.name + ' has logged an entry');

The `WDS.log` object is just a reference to the `Apache Logger` instance, and although all public methods are available on this object, it is recommended the scripter restrict the logging to the following methods:

  • debug(String)
  • info(String)
  • error(String)
  • fatal(String)

Parameters and args

Whenever strings are specified in the `Parameters` field, they are automatically split by the space character and made available as an `args` array. Furthermore, the args string is trimmed to minimise scripting errors. It is also possible to pass in JMeter variables to the Script context. The table below should illustrate this:

JMeter VariableParameterWDS.parameterWDS.args.lengthWDS.args
(None)(None)''0[ ]
(None)hello'hello'1['hello']
(None)hello jmeter'hello jmeter'2['hello', 'jmeter']
suffix='plugins'hello jmeter ${suffix}'hello jmeter plugins'3['hello', 'jmeter', 'plugins']

Declaring JMeter variables is outside the scope of this document. Rather the scripter should refer to the comprehensive documentation on the JMeter website.

Browser

There are already a number of simple example usages for the `WDS.browser` object. This section will now detail some of the more advanced usages of scripting the `WDS.browser` instance.

Form submission

The getting started section has already covered the use case of navigating to a URL. We will now turn our attention to form submission. The script below illustrates the following sequence of events:

  1. Go to a page with a form
  2. Enter characters into the text input fields
  3. Click on the button to submit the form
  4. Verify successful form submission
// 1. Go to a page with a form
WDS.browser.get('http://mobile.yellow.com.au')
// 2. Enter characters into the text input field
var pkg = JavaImporter(org.openqa.selenium)
var what = WDS.browser.findElement(pkg.By.id('what'))
what.sendKeys(['tyres'])
var where = WDS.browser.findElement(pkg.By.id('where'))
where.sendKeys(['melbourne vic'])
// 3. Click on the button to submit the form
var button = WDS.browser.findElement(pkg.By.cssSelector('button.button-search'))
button.click()
// 4. Verify successful form submission
var results = WDS.browser.findElements(pkg.By.cssSelector('ul.search-results-list li'))
if(results.empty) {
    WDS.sampleResult.successful = false
    WDS.sampleResult.responseMessage = 'There were no results returned'
}

The first step of loading a URL has already been covered. The second step is where we expand on the idea of importing all classes from a package and assign it to the `pkg` variable. All classes from the `org.openqa.selenium` packages are now available within the context of this script, and the script above makes use of the By class to access the HTML form fields by their ids.

The first text field is assigned to the `what` variable, and this is just a standard WebElement. We then make use of the `sendKeys` functionality to send a string of characters to this field. We then do the same to the `where` field and send another string of characters to the other field. The astute scripter would have picked up the fact that the string is enclosed within an array. This is on purpose as the Javadoc for the sendKeys method is of type `varargs` which essentially compiles down to an array of strings. For this script to work in Java 6, the scripter must use an array for varargs parameters. However if the scripter is using Java 7, they can omit the array and just use a string. For maximum compatibility, the above script defaults to Java 6 syntax.

The third step introduces another method on the `By` class to allow the scripter to find elements by CSS selectors, and after locating this button, the script will now click on it. Finally, the scripter is responsible for asserting the script completed its actions successfully. This is where we introduce the findElements method which will return a `List` of `WebElements` instead of a single one. This allows the scripter to verify that the list is not empty and that they did indeed land on the search results page.

One thing is missing from the example above, and it is an important one. The timing. The following script enhances the one previously by adding the timing to the relevant sections:

...
// 3. Click on the button to submit the form
var button = WDS.browser.findElement(pkg.By.cssSelector('button.button-search'))
WDS.sampleResult.sampleStart()
button.click()
// 4. Verify successful form submission
var results = WDS.browser.findElements(pkg.By.cssSelector('ul.search-results-list li'))
WDS.sampleResult.sampleEnd()
...

What is important to note is the locations where the timing begins and ends. `sampleStart()` was called before the click - this is obvious. However the corresponding `sampleEnd()` was not called until after the verification of the content on the page. By ensuring that the page has now loaded (ie. by verifying its content) we can now safely assume that the page is loaded and we can now end our timing.

Note: The example above is suitable for a site which generates HTML on the serverside, however for client-side templates, the check for completion could be very different. The next section (AJAX) introduces some more advanced techniques for interrogating the content and status of the page.

AJAX

Testing for AJAX content is one of the more complex parts, and if not scripted properly, it may be quite brittle as well. The following script will automate the following steps:

  1. Go to a web form
  2. Declare variables used for controlling AJAX
  3. Initiate interactions that will cause AJAX content to appear
  4. Verify results
// 1. Go to a page with a form
WDS.browser.get('http://mobile.yellow.com.au')
// 2. Variables used for interacting with AJAX content
var pkg = JavaImporter(org.openqa.selenium, org.openqa.selenium.support.ui)
var what = WDS.browser.findElement(pkg.By.id('what'))
var wait = new pkg.WebDriverWait(WDS.browser, 10)
// 3. Enter characters and wait for the AJAX content to display - timing out at 10 seconds
what.sendKeys(['ty'])
WDS.sampleResult.sampleStart()
wait.until(pkg.ExpectedConditions.presenceOfElementLocated(pkg.By.cssSelector('ul.suggestions')))
WDS.sampleResult.sampleEnd()
// 4. Verify successful form submission
var results = WDS.browser.findElements(pkg.By.cssSelector('ul.suggestions li'))
if(results.empty) {
    WDS.sampleResult.successful = false
    WDS.sampleResult.responseMessage = 'There were no suggestions'
}

The script above is quite similar to the Form submission example, but there are a few new concepts that should be covered.

The first thing to note, is that the JavaImporter allows multiple packages to be imported and aliased to a single javascript variable. This allows the ExpectedConditions class and WebDriverWait class to be declared. These classes are used to wait for AJAX content to appear.

When Declaring the WebDriverWait, the second parameter is the maximum amount of time in seconds to wait for the AJAX content to appear. If nothing appears the default behaviour of this class is to throw a NoSuchElementException, which can be suppressed if desired.

After all the required variables are declared, we will now enter some text into the input field and wait for the AJAX content to display. This is where we will now wait (up to 10 seconds) for the HTML list to appear. It is also important to note that we include the requisite calls to WDS.sampleResult.sampleStart() and WDS.sampleResult.sampleEnd() to track the timing.

Finally we ensure that the list returned via. AJAX contains content. Failing otherwise. For more details on AJAX and Selenium WebDriver, it would be useful for the scripter to refer to the Selenium Advanced Usage page.

Resource Footprint

Because of running real browser, WebDriver tests require a lot of resources. In general case, you need 1 CPU core per virtual user with it. This makes difficult to scale WebDriver test to have hundreds and thousands of vurtual users. However, the load testing cloud providers may help to scale WebDriver test up to thousands of real browsers, look at BlazeMeter for example.