Convert a LoadRunner script to a Gatling simulation
Instructions
Step 1: Find the LoadRunner script, configuration, and parameters files
- Search for LoadRunner script files:
Action.c,vuser_init.c,vuser_end.c: the standard three-file layout- Any
.cor.hfiles in a LoadRunner script directory (look for a.usror.lrsdescriptor file nearby) - A single combined
.cfile containing all sections
- If multiple scripts or directories are found, ask the user to specify which one to convert
- Read all relevant
.cand.hfiles before proceeding - Read all relevant
.datfiles as plain text files - Search for a
default.cfgfile in the same directory and read it if present - Search for a
.prmfile in the same directory and read it if present
Step 2: Specify output
Either find an existing Gatling project or initialize a new one:
- Try to find an existing project with the /gatling:gatling-detect-existing-project skill
- If no existing project is found, offer to create a new one with the /gatling:gatling-bootstrap-project skill
Step 3: Conversion
- Convert the LoadRunner script to a Gatling test written in the specified language
- Write the output to the appropriate source directory of the Gatling project
Preamble
Java SDK
IF a variable needs to be saved inside the function AND the function is used within an Expression Language string:
- Move the code to an exec block that allows saving variables
When parsing dates, use java.time.format.DateTimeFormatter.ofPattern with system default zone and store it
outside a function to avoid creation cost overhead.
For regular expression, use java.util.regex.Pattern.compile and store it outside a function to avoid creation cost
overhead.
Gatling DSL
Be aware of DSL differences between all the languages Gatling support.
Some methods needs to be followed by .on() in Java, JavaScript and Kotlin but not Scala:
exitBlockOnFail { ... }:exitBlockOnFail().on(...)group("name") { ... }:group("name").on(...)- All loops, etc.
Session handling is different, e.g.:
session.getString("name")in Java, JavaScript and Kotlin,session("name").as[String]in Scala
Script structure
A LoadRunner script has up to three logical sections. Map them as follows:
vuser_init:beforeblock on the simulationAction: one or morescenario+execvuser_end:afterblock on the simulation
If the script only has an Action section (or a single .c file), produce a simulation with just a scenario.
Runtime settings from default.cfg
IF a default.cfg file is present for runtime settings that affects HTTP behavior:
Check the [General] section:
AutomaticTransactions=1and/orAutomaticTransactionsPerFunc=1: LoadRunner can auto-wrap actions or function calls into transactions. In Gatling, scenario and request names serve this purpose directly; nogroup()blocks are needed hereDefaultRunLogic: read the file, checkRunLogicActionOrderin the[RunLogicRunRoot]section actions order, chain them withexecContinueOnError=1: addexitHereIfFailed()between requests, otherwise Gatling continues on error by default; mention this to the userFailTransOnErrorMsg=1: apply the logic oflr_error_messageas Gatling checks if possible, wrap the transaction inside aexitBlockOnFail()block
Check the [ThinkTime] section:
Options=NOTHINK: keep pauses times but add.disablePauses()tosetUpOptions=RECORDED: keep pauses timesOptions=MULTIPLY: keep original pauses times, Gatling does not have a direct equivalent; mention this to the userOptions=RANDOM: keep pauses times, useThinkTimeRandomLowandThinkTimeRandomHigh, add.uniformPauses()tosetUpwith the closest number that matches the original plus or minus values
Check the [WEB] section:
CustomUserAgent: add.userAgentHeader()tohttpProtocolSearchForImages=1: add.inferHtmlResources()tohttpProtocol
HTTP requests
web_url(name, url, ...):http(name).get(url)web_submit_data(name, ...):http(name).post(url).formParam(...)web_submit_form(name, ...):http(name).post(url).formParam(...)web_custom_request(name, method, url, ...):http(name).httpRequest(method, url)web_add_header(name, value):.header(name, value)web_add_auto_header(name, value):httpProtocol.header(name, value)or per-request.header(name, value)
web_add_header is one-shot: it applies only to the immediately following request, then is cleared; never hoist it into httpProtocol.
web_add_auto_header persists from the point of the call onward. If it appears before any request in the script, map it to httpProtocol.header(...); if it appears mid-script, add .header(...) to each request that follows it.
For web_submit_data, extract each ITEMDATA name/value pair as a .formParam(name, value).
Response checks and correlation
web_reg_find(text=...):.check(bodyString.contains(...))web_reg_save_param(param, LB=, RB=):.check(regex("LB(.*?)RB").saveAs("param"))web_reg_save_param_ex(...):.check(regex(...).saveAs(...))or.check(xpath(...))web_reg_save_param_json(...): convertQueryStringtojmesPathas.check(jmesPath(...))
Place .check(...) calls on the request that triggers the response being checked.
web_reg_* functions are registered before the request they apply to, find the next web_url/web_submit_data and attach the check there.
Parameters and session variables
{ParamName}(LR parameter substitution):#{paramName}(Gatling EL)lr_save_string(value, "param"):.exec(session -> session.set("param", value))lr_param_sprintf("param", fmt, ...):.exec(session -> session.set("param", ...))with string formattinglr_eval_string("{param}"):session.getString("param")or"#{param}"in EL strings
For parameter files (.dat files referenced in the script or .usr descriptor), convert them to Gatling feeders.
Copy the data file to the Gatling project's resources directory.
IF a .prm file is present, check it for each [parameter:<parameter name>] entries:
ColumnName="Col <number>":ColumnName="<column name>":Delimiter="<char>": usecsv,tsv, orseparatedValues("<file path>", '<char>')SelectNextRow="Random": use.random()SelectNextRow="Same line as <parameter name>": ensure configuration is the same as<parameter name>SelectNextRow="Sequential": use.circular()SelectNextRow="Unique": use.queue()but it has no real Gatling equivalent; mention this to the user
Transactions
In LoadRunner, transactions are the only way to name and track individual requests, so developers routinely wrap every single request in a transaction. In Gatling, HTTP requests are named and tracked individually in reports, making single-request groups redundant.
Apply this rule when mapping lr_start_transaction / lr_end_transaction pairs:
- Single-request transaction (the block contains exactly one HTTP request and no nested transactions): drop the
group()wrapper entirely. Use the transaction name as thehttp("name")request name instead. - Multi-request transaction (the block contains more than one HTTP request, nested transactions, think times, or session logic): map to a
group("name")block as usual.
When FailTransOnErrorMsg=1 applies to a single-request transaction, wrap the request directly in exitBlockOnFail() without a surrounding group().
Logging
lr_output_message,lr_log_message: remove these or convert to a comment, Gatling handles logging at the framework levellr_error_message: keep, wrap inside anexecblock with session and use.markAsFailed()on session if necessary
Rendezvous points
lr_rendezvous("name"): remove, Gatling does not have a direct equivalent; mention this to the user if present
Resource files
Any files referenced in the script (e.g., request body files, upload files, data files) should be copied to the Gatling project's resources directory and referenced via RawFileBody("filename") or a feeder.
Step 4: Verify the code compiles
Use the build-tool skill if available.
Step 5: Post conversion
After the conversion, inform the user of:
- Any LoadRunner features that have no direct Gatling equivalent (rendezvous points, IP spoofing, etc.) and suggest alternatives
- Any hardcoded credentials or environment-specific values found in the script that should be parameterized
- Possible Gatling-idiomatic improvements, such as:
- Extracting the
HttpProtocolconfiguration (base URL, common headers) into a sharedhttpProtocolobject - Replacing repeated
formParamblocks with a map if there are many parameters - Using feeders for any remaining hardcoded user data
- Extracting the