100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

BeanShell and JSR223 Scripting

Learn how JMeter's scripting elements let you inject custom Java-like or Groovy logic into a test plan, and why JSR223 with Groovy has replaced BeanShell as the recommended approach.

Scripting & PluginsIntermediate9 min readJul 10, 2026
Analogies

Why Scripting Elements Exist in JMeter

JMeter's built-in samplers and configuration elements cover most load testing needs, but real applications often require logic that no dropdown can express: computing an HMAC signature, walking a nested JSON response to pull three unrelated fields, or branching test flow based on a previous response. Scripting elements (PreProcessors, PostProcessors, Samplers, and Assertions built on a scripting engine) let you drop arbitrary code directly into the test plan tree at exactly the point it needs to run, with full access to JMeter's internal variables, thread context, and response data.

🏏

Cricket analogy: Just as a captain like Rohit Sharma occasionally calls for a non-standard field placement that isn't in the coaching manual to counter a specific batter, a scripting element lets you insert custom logic outside JMeter's standard sampler toolkit.

BeanShell Sampler and PreProcessor

BeanShell was JMeter's original scripting engine, exposing a near-Java syntax with dynamic typing through the bsh library. A BeanShell Sampler, PreProcessor, or PostProcessor gives you access to built-in objects like vars (JMeterVariables), props (JMeter properties), log, ctx (JMeterContext), and prev (the previous SampleResult). The catch is that BeanShell interprets the script fresh on every single sample unless you explicitly cache the interpreter, which makes it noticeably slower under high thread counts and large iteration counts than compiled alternatives.

🏏

Cricket analogy: Relying on BeanShell without caching is like a bowler re-marking his run-up from scratch before every single delivery instead of settling into a rhythm, wasting time that adds up over a full spell.

java
// BeanShell PreProcessor: sign a request with a timestamp-based token
import org.apache.commons.codec.digest.DigestUtils;

String timestamp = String.valueOf(System.currentTimeMillis());
String secret = props.get("api.secret");
String raw = timestamp + secret;
String token = DigestUtils.sha256Hex(raw);

vars.put("authTimestamp", timestamp);
vars.put("authToken", token);
log.info("Generated auth token for thread " + ctx.getThreadNum());

JSR223 and Groovy Performance

JSR223 elements (JSR223 Sampler, PreProcessor, PostProcessor, Assertion, Timer) use the javax.script API and support multiple languages, but Groovy is the JMeter team's official recommendation because it ships bundled since JMeter 3.1 and its scripts are compiled and cached by default, giving performance close to native Java. The 'Compilation Cached' checkbox present on JSR223 elements controls this behavior, and leaving it enabled means the Groovy engine only recompiles when the script text actually changes, not on every loop iteration.

🏏

Cricket analogy: It's like a bowler who has bowled the same yorker variation thousands of times in the nets, so on match day the action is automatic rather than something he has to consciously rebuild each ball.

Since JMeter 3.1, the JMeter team explicitly recommends replacing BeanShell elements with JSR223 + Groovy wherever possible. Groovy is bundled with JMeter, supports the same vars/props/log/ctx/prev API, and with script caching enabled it is typically 5-10x faster than BeanShell under sustained load.

Common Use Cases: Dynamic Correlation and Custom Assertions

The two most common real-world uses for scripting elements are dynamic correlation and custom assertions. A JSR223 PostProcessor can parse a response with JsonSlurper or a regex, apply business logic (like picking the cheapest of several returned product IDs), and store the result with vars.put() for later samplers to consume. A JSR223 Assertion goes beyond JMeter's built-in Response or Duration Assertions by letting you validate cross-field business rules, such as confirming a returned order total equals the sum of line items, and calling AssertionResult.setFailure(true) with a custom message when the rule is violated.

🏏

Cricket analogy: A third umpire reviewing ball-tracking data to check leg-before-wicket against multiple criteria at once, not just one simple rule, is like a custom assertion validating several business conditions in a single check.

groovy
// JSR223 PostProcessor: extract cheapest product ID from a JSON array response
import groovy.json.JsonSlurper

def json = new JsonSlurper().parseText(prev.getResponseDataAsString())
def cheapest = json.products.min { it.price }

vars.put("cheapestProductId", cheapest.id.toString())
vars.put("cheapestPrice", cheapest.price.toString())

// JSR223 Assertion: verify order total equals sum of line items
def order = new JsonSlurper().parseText(prev.getResponseDataAsString())
double sum = order.lineItems.sum { it.price * it.quantity }
if (Math.abs(sum - order.total) > 0.01) {
    AssertionResult.setFailure(true)
    AssertionResult.setFailureMessage("Total mismatch: expected " + sum + " got " + order.total)
}

Scripts run inside the sampler's own thread, so anything stored with vars is thread-local and safe. But props (JMeter properties) are shared across all threads and the entire JVM, so writing to a prop from inside a loop without synchronization can produce race conditions under concurrent load. Use props only for values that are genuinely global and rarely written, such as a run-wide feature flag set once in setUp Thread Group.

  • Scripting elements (PreProcessor, PostProcessor, Sampler, Assertion, Timer) let you inject Java-like or Groovy code at any point in the test plan tree.
  • BeanShell is the legacy engine; it interprets scripts fresh on every sample unless the interpreter is manually cached, which limits throughput.
  • JSR223 is the modern replacement API, and Groovy is JMeter's officially recommended language for it because it ships bundled and compiles scripts by default.
  • Leaving 'Compilation Cached' enabled on JSR223 elements avoids recompiling unchanged scripts on every iteration, giving near-native Java performance.
  • vars, props, log, ctx, and prev are the standard objects exposed to scripts for reading/writing variables, properties, logging, thread context, and the previous sample result.
  • Typical use cases are dynamic correlation (parsing responses to extract and reuse values) and custom assertions (validating cross-field business rules).
  • vars is thread-local and safe to mutate freely; props is shared across all threads and should be written to sparingly and carefully.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#JMeterStudyNotes#TestingQA#BeanShellAndJSR223Scripting#BeanShell#JSR223#Scripting#Elements#StudyNotes#SkillVeris