Originally written by Patrick Lightbody

Neustar’s Web Performance Management (WPM) supports the ability to pass in parameterized data in your load test scripts. This is done by adding a small amount of JavaScript in your test. Whether you are working with a Real Browser User (RBU) script or a Virtual User (VU) script, we recommend first creating a script with fixed data and then modifying it to include data parameterization after you’ve imported your Selenium scripts (for RBUs) or converted to a VU script. In this document we’ll look at a few ways to parameterize your test:

  • Basic Parameterization: using a random value from a collection of values
  • Using a CSV File: accessing data in an external file
  • Unique Parameterization: ensuring that no two values are used in overlapping script executions
  • Time-Based Uniqueness: using timestamps to ensure uniqueness of data
  • Sequential Uniqueness: using counters provided by the BrowserMob API to ensure uniqueness

Consider the following basic RBU script that uses a fixed username and password of “joe” and “password”:

var selenium = browserMob.openBrowser();

browserMob.beginTransaction();
browserMob.beginStep(“Step 1″);

selenium.open(“http://example.com/login”);
selenium.type(“username”, “joe”);
selenium.type(“password”, “password”);
selenium.clickAndWait(“login”);

browserMob.endStep();
browserMob.endTransaction();

Let’s see how we can modify this script to include different styles of parameterization.

Basic Parameterization

If you want to type a random string in to a field, you first need to create the list of strings you’d like to use. For example, if the field is a username field for a login form, you may want to generate a list of 10, 100, or even 1000 usernames to use in the load test. Once you have the list, you’ll need to add it to your script. You do so by using a standard JavaScript array with some simple code that selects a random value from the array and uses it to type the value in. Let’s see how our example changes using this technique:

var selenium = browserMob.openBrowser();

var usernames = ['joe', 'meg', 'pat', 'chris', 'rob', 'beth'];

browserMob.beginTransaction();
browserMob.beginStep(“Step 1″);

var index = Math.floor(Math.random() * usernames.length);
var username = usernames[index];

selenium.open(“http://example.com/login”);
selenium.type(“username”, username);
selenium.type(“password”, “password”);
selenium.clickAndWait(“login”);

browserMob.endStep();
browserMob.endTransaction();

Note how we select a random index from the array by using the Math.floor() and Math.random() functions. And because we base the random number on the length of the usernames array (ie: usernames.length), we can always add additional usernames to the array and they will automatically be included as part of the random selection.

Parameterization Using an External CSV File

If you have a lot of data, it may not be convenient to try to represent it in a JavaScript array like we did in the last example. That’s why WPM also supports reading data from an external CSV file:

var selenium = browserMob.openBrowser();

var csv = browserMob.getCSV(“logins.csv”);
var row = csv.random();

browserMob.beginTransaction();
browserMob.beginStep(“Step 1″);

var username = row.get(“username”);
var password = row.get(“password”);

selenium.open(“http://example.com/login”);
selenium.type(“username”, username);
selenium.type(“password”, password);
selenium.clickAndWait(“login”);

browserMob.endStep();
browserMob.endTransaction();

As you can see, the “csv” object has a random() method on it, which returns a random row. Once you have a handle to the row, you can call get() on it by passing in the column label. The CSV simply needs to be in this format:

“username”,”password”
“joe”,”password”
“meg”,”password”
“pat”,”password”
“chris”,”password”
“rob”,”password”
“beth”,”password”
…

The “csv” object also offers the ability to determine the number of records in it by calling size(). You can also get a specific row by calling get(X) on the csv object, where X is a number between 0 (inclusive) and the return value of size() (exclusive).

You will be asked to upload the CSV after you save the script.

Forcing Unique Parameterization

Sometimes it’s necessary to pin data, such as a username, to a specific RBU or VU script execution. This is often required in financial systems and other high security applications that prohibit the same username to be used with multiple overlapping sessions. In these cases, random selection is not good enough, since it leaves open a chance of an artificial test failure. The solution to this problem is to support locking parameterized data to each virtual user. Then if you have 100 VUs in a test, then you’d have an array of 100 usernames, each one assigned a virtual user. BrowserMob supports this technique using the getUserNum() function in the browserMob object:

var selenium = browserMob.openBrowser();

var usernames = ['joe', 'meg', 'pat', 'chris', 'rob', 'beth'];

browserMob.beginTransaction();
browserMob.beginStep(“Step 1″);

var username = usernames[browserMob.getUserNum()];

selenium.open(“http://example.com/login”);
selenium.type(“username”, username);
selenium.type(“password”, “password”);
selenium.clickAndWait(“login”);

browserMob.endStep();
browserMob.endTransaction();

As you can see, we’re no longer using the Math functions to randomly select a username. Instead, we use the value of getUserNum() for the index selection. This variable will return 0 for the first RBU or VU in a load test, 1 for the next RBU or VU, etc. It will remain the same for each RBU/VU script execution for that same RBU/VU. This means that no two scripts will ever execute at the same time with the same username.

Alternatively, if your usernames can be predictable (ie: user1, user2, user3, etc) you might find this even easier:

var selenium = browserMob.openBrowser();

browserMob.beginTransaction();
browserMob.beginStep(“Step 1″);

selenium.open(“http://example.com/login”);
selenium.type(“username”, “user” + browserMob.getUserNum());
selenium.type(“password”, “password”);
selenium.clickAndWait(“login”);

browserMob.endStep();
browserMob.endTransaction();

As you can see in this example, an email address variable is assigned with an email that contains the value of “new Date().getTime()”. This function call returns a series of numbers that represents the total number of milliseconds since 1970. Since that number is obviously increasing as time goes on, you can be reasonably confident that no two email address will be the same in your load test – provided that no two scripts run at exactly the same moment in time (a very rare occurrence when measuring down to the nearest millisecond).

However, if you do want to guarantee uniqueness, you can extend this example by encoding both the timestamp and the value of browserMob.getUserNum():

var email = “test-” + browserMob.getUserNum()
            + “-” + new Date().getTime()
            + “@example.com”;

Sequential Uniqueness

In even more advanced situations, sometimes a timestamp won’t work. In these cases, you can use a combination of browserMob.getUserNum() and browserMob.getTxCount() to either select unique data from a collection or to generate a more predictable unique value. The getTxCount() function returns an increasing count for each RBU or VU in the test.

It is not the global transaction count, but rather the number of transactions that have been executed (success or failure) for each unique value returned by getUserNum(). Using these two values, you can construct a unique string or integer that can be used in the test to meet your requirements.