Keyword Development

Edited on December 14, 2016

Keyword concept

Keywords are the primary building block in the design of test plans. Keywords are objects which carry the mapping between a piece of business logic in an application and the corresponding simulation routine, in other words, the script that simulates the user interactions with that part of the application. It's a fundamental notion in STEP.

For example, one could use a series of 3 Keywords called "CreateContract", "ServiceCustomer" and "BillCustomer". Each of these Keywords would then be mapped to three different scripts, CreateContract.js, ServiceCustomer.js and BillCustomer.js. Together, these routines could then be combined into a Test Plan to build an End-to-End Business Case for a certain type of customer.

There are two types of keywords : handler and composite keywords.


  • Handler

    A handler or "concrete keyword" is an object which represents a script or part of a script file (an annotated method in the case of Selenium, for instance). You will need to create a new keyword for each script that you want to declare and execute in STEP.

  • Composite

    Composite keywords are an advanced feature of step. They are meant to improve the maintainability and readability of TestPlans. We recommend that beginners focus on Handler keywords early on.
  • A composite keyword or "logical keyword" is an object which contains references to other keywords. The goal of a composite keyword is to organize and provide a form of alias for groups of keywords which are often executed together and in the same order. Technically it is similar to a test plan but storing groups of keywords as a composite keyword will help you write cleaner and more modular test suites, instead of clumping up everything in a single test plan. It can also be used to wrap a keyword with a specific set of inputs and output-checks, thus isolating a specific test case into its own keyword.

In our example, the keyword "CreateContract" could be a composite keyword made out of a series of 5 concrete keywords called "Login" > "CreateCustomer" > "CreateContractForCustomer" > "ValidateContract" > "Logout".

A functional tester who wishes to validate the application behavior for certain inputs does not need to know how these concrete keywords are implemented. And by grouping these keywords into a Composite Keyword, his Test Plan remains simple and easily readable. At exectution time however, for example in the event of a failed test, all of the execution details of the different sub-keywords remain available and are displayed by default.

Keyword configuration

  • When creating a handler keyword, you need to provide a handler string which is composed as follows :
    • Default engine (javascript)

If you wish to use the default javascript engine, all you need to provide as a handler string is the STEP class you wish to use to load and interpret the script, which by default will be :


The reason we're using this mechanism is that anyone can create and use a custom handler to load and execute any other type of scripting engine, simulation tool or third party library. However, we will most likely build default configuration profiles in the next release to ease the configuration process and so that the end user could simply choose said profile from a drop down list.

  • Selenium

If you wish to use selenium, then the handler string will comprise three tokens:

  • the path to the selenium libraries to be used by that keyword if you wish to override the default library version which is 3.0
  • the path to the script repository containing the corresponding compiled test classes and methods
  • the STEP handler class, which should be by default : step.script.AnnotatedMethodHandler

The result will look like this :


For example, if the libs folder of my selenium library is stored there :


and the path to my compiled test classes is :


then the correct input is:


As mentionned above, this mechanism will be hidden from the end user in the next release. Handler profiles will be created once, default profiles will be provided, and the user will simply choose on what technology profile the keyword is supposed to run.

  • Composite keywords require no configuration and can be built just like test plans. Please refer to the section "Building Test Plans" to learn how to build a test plan.

Keyword execution

  • Remote execution (using a STEP Controller and Agent)

    • Standalone keyword execution

      • // TODO

    • Executing a Keyword as part of a test plan

      • In order to execute a keyword, you need to create a "Plan" and reference the keyword in the tree. See section "Building Test Plans" for more information on this topic.

  • Local execution of Selenium Keywords with STEP

    • In the development phase, you can use STEP libraries to execute your Selenium script and hence STEP Keyword locally, as if it were running on the server.

      • The STEP API libraries are compiled into a jar file called step-dev-X.Y.Z.jar, available on the release download page on github, here : .

      • You can then use the ScriptContext API to pass arguments to your Keyword. Please take a look at the example provided below in the paragraph "Selenium keywords".

Make sure to reboot your agent if you make any changes to the scripts (compiled classes). Since the classes are cached in the JVM, any update made won't be effective until the agent's JVM is rebooted. If you don't want to wait out the 1 minute grace period during which the controller waits for the agent to reconnect, reboot the controller as well.

Default keyword examples

Our distribution of STEP ships with a many demo keywords which show how a handler keyword and the corresponding script are supposed to be configured when executing on the default scripting engine (javascript). Those keywords are called "Demo_Echo" and "Demo_HTTPGet".


The scripts are located inside the data/scripts folder of the controller and the path to the script folder is determined by the variable "scripthandler.script.dir" which is set in the Test Plan "Demo_TestCase_HTTPGet" under the node Set.


Selenium keywords

When using the AnnotatedMethodHandler, STEP will automatically look for a method annotated with the name of the keyword. Therefore you need to use the "@Function" annotation to set the name of the keyword in you class.

In order to pass STEP inputs to a selenium method, you can use the @Arg annotation which is part of the STEP API. The STEP dependencies required to use this API are contained in the dev archive which is also available as part of the github release : step-dev-X.Y.Z.jar.

This mechanism works regardless of whether you're using Selenium (this means you could implement and run plain Java "Functions" without Selenium). In the case of Selenium test methods, we've made an abstract class available to help with details such as driver management called AbstractSeleniumScript. These classes are already part of the agent libraries so you don't need to worry about the server side execution details, you just need the dev jar file when developping locally to simulate step's execution context.

The @Prop annotations are used to inject agent configuration properties into the script context. The configuration of the phantomjs binary path works as an example of this mechanisme. See section "Installation" of this documentation.

Make sure to reboot your agent if you make any changes to the scripts (compiled classes). Since the classes are cached in the JVM, any updates made won't be effective until the agent's JVM is rebooted. If you don't want to wait out the 1 minute grace period during which the controller waits for the agent to reconnect, reboot the controller as well.

Here is a sample selenium script which we've mapped with a keyword called "MyPortal_Home" and which uses the htmlunit driver :


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.interactions.Actions;

import step.script.Arg;
import step.script.Function;
import step.script.Prop;
import step.script.selenium.AbstractSeleniumScript;

public class MyPortalLibrary extends AbstractSeleniumScript {

       public void login(@Arg("url") String url, @Prop("phantomjs.binary.path") String phantomJsBinary) throws Exception {

        setDriver(new HtmlUnitDriver());

           setDriver(new FirefoxDriver(
          new FirefoxBinary(new File("D:\\Users\\user\\Downloads\\FirefoxPortable\\App\\Firefox64\\firefox.exe")),
          new FirefoxProfile()));

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);



Notice : Since v3.2, you don't actually have to put the name of your Keyword inside of the @Function annotation. If there is no "name" attribute inside of the annotation, step will try to resolve the symbolic name of the method itself instead.


Note that the startMeasure and stopMeasure calls will use timers to measure and save the performance metrics in the mongodb database. These results can then be analyzed in RTM, by following the link "Analyze in RTM" of the Execution view of a given test execution.

You can then call your selenium keyword either via the STEP server instance (via a direct keyword or test plan execution) or locally as a Junit Test without running any controller or agent by following this example :

    public void testMyPortal_Home() {
        Map<String, String> properties = new HashMap<>();
        // if using phantomjs locally
        // properties.put("phantomjs.binary.path", "/my-path/phantomjs");

        ScriptContext ctx = ScriptRunner.getExecutionContext(properties);"MyPortal_Home", "{\"url\":\"\"}");



Example of Selenium keyword configuration in STEP :



Passing and accessing configuration variables from Java

  • You can either centralize a variable to make it accessible to all Agents or tokens :in this case just set it in the controller's configuration file called Parameter.js (under conf)
    • You can then access it through the object "message" inside of your script
  • Or set it on a per-agent basis in the AgentConf.json configuration file of the agent (under conf)
    • You can then access it through an annotated method argument (@Prop)
  • See screenshot below for both examples.