Skip to main content

Selenium Virtual User Scripts

Selenium Virtual User (VU) scripts are written in Java and use Selenium WebDriver to automate modern browsers. Each VU owns a single WebDriver instance and runs in headless mode by default for scalability. Headless execution in Selenium 4 uses the native headless modes of modern browsers (e.g., Chrome Headless), providing stability and compatibility with contemporary web apps.


1) The Selenium VU programming model

  • Your custom VU extends SeleniumVirtualUser and configures the browser in pre().

  • Scripts get the wrapped driver via getWebDriver() and interact with pages using Selenium APIs.

  • The Eggplant wrappers (WebDriver, WebElement) add:

    • short, configurable readyState waits after navigations (default ~400 ms)

    • resilient clicking with minimal retries

    • automatic navigation + performance metrics capture (CDP if enabled; JS Navigation Timing fallback)

Minimal custom VU (Chrome example)

package com.example;

import com.facilita.fc.selenium.SeleniumVirtualUser;

public class ChromeDriverVU extends SeleniumVirtualUser {
@Override
public void pre() throws Exception {
super.pre();
// Balanced defaults
getVU().setWaitForReady(true)
.setReadyWaitMs(400)
.setCdpMetricsEnabled(false)
.setCdpBudgetMs(200)
.setClickRetries(2)
.setRecordPostClickNav(true)
.setNavDedupWindowMs(300);

initialiseChromeDriver();
}
}

Using Headless Chrome via the "HtmlUnit" path

@Override
public void pre() throws Exception {
super.pre();
getVU().setWaitForReady(true)
.setReadyWaitMs(400)
.setCdpMetricsEnabled(false)
.setClickRetries(2);
// In our integration, this initialiser creates a Headless Chrome driver
initialiseHtmlUnitDriver();
}

Using Firefox with a stable server profile

import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;

@Override
public void pre() throws Exception {
super.pre();
getVU().setWaitForReady(true).setReadyWaitMs(400).setClickRetries(2);

FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("dom.ipc.processCount", 1);
profile.setPreference("gfx.webrender.force-disabled", true);
profile.setPreference("layers.acceleration.disabled", true);

FirefoxOptions options = new FirefoxOptions();
options.setProfile(profile);
options.setAcceptInsecureCerts(true);
initialiseFirefoxDriver(options);
}

Using Microsoft Edge

@Override
public void pre() throws Exception {
super.pre();
getVU().setWaitForReady(true).setReadyWaitMs(400).setClickRetries(2);
initialiseEdgeDriver();
}


2) Writing scripts with the wrapped WebDriver

Basic navigation and interactions

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

getWebDriver().get("https://example.com");

WebElement user = getWebDriver().findElement(By.id("username"));
user.clear();
user.sendKeys("demo");

getWebDriver().findElement(By.cssSelector("button.login")).click();

Robust element selection with fallbacks

// Prefer stable locators first
By primary = By.id("search");
By fallback = By.cssSelector("input[name='q']");

org.openqa.selenium.WebElement box;
try {
box = getWebDriver().findElement(primary);
} catch (org.openqa.selenium.NoSuchElementException e) {
box = getWebDriver().findElement(fallback);
}
box.sendKeys("selenium");

Handling post-click navigations automatically

The wrapped WebElement.click() records a request if the URL changes and collects metrics without additional code in your script.


3) Navigation API behavior

All navigation methods (get(url), back(), forward(), refresh(), to(...)) now:

  1. start a request,

  2. perform the navigation,

  3. optionally apply a short document.readyState wait (if enabled),

  4. end the request with metrics capture.

refresh() always records a request even if the URL string is unchanged.


4) When to enable CDP metrics

  • Keep off for regression speed.

  • Enable when you need deeper Network/Performance breakdowns; capture waits are capped (e.g., 200 ms) before falling back to JS Navigation Timing.


5) FAQs

Q: Do I need to set driver paths?
A: Usually no in Selenium 4: driver binaries can be auto‑resolved. Only pin paths when debugging specific nodes.

Q: Is "HtmlUnit" still the legacy HtmlUnit browser?
A: In this integration, initialiseHtmlUnitDriver() creates Headless Chrome for modern compatibility and stability.