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
SeleniumVirtualUserand configures the browser inpre(). -
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:
-
start a request,
-
perform the navigation,
-
optionally apply a short document.readyState wait (if enabled),
-
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.