Pre-Processors, Post-Processors, Extractors, and Correlation: Core Concepts and Mental Model
Chapter 08 proved that responses are functionally correct. Chapter 09 makes multi-step requests causally correct: values emitted by one response are extracted after that sample, stored in that virtual user's variable state, and used to build the next request instead of replaying one token captured during recording.
Learning objectives
- Place Pre-Processors and Post-Processors from execution semantics rather than visual ordering.
- Explain correlation as response-derived, thread-local state flowing into a later request.
- Distinguish a JMeter variable from a shared JMeter property for per-user session data.
- Choose a dedicated extractor before reaching for JSR223 code.
- Define an explicit missing-extraction sentinel and correctness check.
- Inspect correlation without leaking real credentials or session identifiers.
1. Why recorded tokens make invalid multi-user scripts
A browser recording often captures a concrete CSRF token, order ID, request ID, or session handle. Replaying that literal value may work for one request and fail as soon as the server rotates state or several users execute concurrently. Worse, a test can send the same captured session identity from every JMeter thread and accidentally benchmark one shared server-side session.
http://127.0.0.1:8000, at most 3
threads, and at most 3 loops. Never print, commit, or copy real
bearer tokens, CSRF values, session cookies, authorization headers,
or customer identifiers into course artifacts.
2. Mental model: correlation follows causality
Follow the arrows as a causal data flow. The response exists before the extractor can create a variable, and the downstream request is constructed from that thread's own variable state.
flowchart TD A[Sampler A: start session] --> R[Response contains synthetic token] R --> X[Post-Processor / extractor] X --> V[Thread-local CORR_TOKEN] V --> P[Pre-Processor prepares PREPARED_TOKEN] P --> B[Sampler B: use session] B --> T[Authorized local target] T --> RR[Response / status] RR --> Q[Assertion verifies accepted] V -. thread 1 copy .-> U1[Virtual user 1] V -. separate thread 2 copy .-> U2[Virtual user 2]
Sampler A must complete before its Post-Processor
can read the response. The extractor writes
CORR_TOKEN into the current thread's JMeter variable
map. Immediately before Sampler B, a Pre-Processor reads that same
thread's token and derives PREPARED_TOKEN. Sampler B
sends both values. An assertion then verifies the business outcome.
Thread 1 and thread 2 have separate variable maps, so their
correlated values do not overwrite one another.
3. Execution order: scope first, type order second
For one applicable sampler, JMeter's current execution order is: Configuration elements → Pre-Processors → Timers → Sampler → Post-Processors → Assertions → Listeners. A Post-Processor therefore cannot inspect assertion results for the same sample because assertions have not run yet.
Visual sibling order does not turn a Post-Processor into “after the sampler above it.” If a processor is scoped broadly under a controller, it can run for every sampler in that scope. Attach it as a child of the specific sampler when correlation belongs only to that response/request.
4. Pre-Processor versus Post-Processor
| Element | Runs | Typical correlation role | State touched |
|---|---|---|---|
| Pre-Processor | Immediately before an in-scope sampler. | Prepare/transform variables or request data required by the upcoming sample. | Current thread variables and/or current sampler configuration. |
| Post-Processor | Immediately after an in-scope sampler, before assertions. | Extract response-derived IDs/tokens into variables for future requests. | Previous/current SampleResult and current thread variables. |
| Assertion | After Post-Processors. | Reject missing/incorrect extraction outcome or downstream business result. | Sample success/failure evidence. |
5. Correlation values belong in variables, not global properties
Chapter 06 established that JMeter variables are normally
thread-local while JMeter properties are shared by the entire JMeter
process. A response-derived user token is therefore a variable.
Writing it to props or
__setProperty creates a cross-thread race and can make
users reuse one another's session state.
6. Dedicated extractors should be the default
Current JMeter includes built-in Regular Expression, CSS Selector, XPath/XPath2, JSON Extractor, JSON JMESPath Extractor, and Boundary Extractor Post-Processors. Use the extractor that matches the response format. Dedicated components are easier to review and generally cheaper than arbitrary script code.
This chapter uses JSON JMESPath Extractor with expression
session.token. Chapter 10 goes deeper into regex,
CSS/JQuery, XPath, JSONPath, and boundary extraction choices.
7. JSON JMESPath Extractor contract
A JSON JMESPath Extractor runs after the sampler in its scope and can store a selected result in a named JMeter variable. Configure:
- Apply to: Main sample only
- Name of created variable:
CORR_TOKEN - JMESPath:
session.token - Match No.:
1 - Default Value:
CORR_MISSING
The explicit sentinel is intentional. If the response changes shape,
CORR_TOKEN becomes CORR_MISSING rather
than silently keeping some stale token.
8. Missing correlation must be observable
Do not let a failed extraction fall through as an unexplained
literal ${CORR_TOKEN} or a previous value. During
authoring, set a clear default and assert that the extracted
variable is not the sentinel. In load mode, preserve the failed
sample/JTL and stop or skip the remaining dependent steps according
to an explicit error policy.
9. JSR223 PreProcessor is for transformation, not basic extraction
A Pre-Processor is appropriate when the next request requires a derived form that a normal JMeter field/function cannot express clearly. The mandatory lab converts the synthetic token to uppercase only to prove the processor ran before the next sampler:
def token = vars.get('CORR_TOKEN')
if (token == null || token == 'CORR_MISSING' || token.trim().isEmpty()) {
vars.put('PREPARED_TOKEN', 'PREP_MISSING')
vars.put('CORRELATION_STATE', 'missing')
} else {
// Synthetic transformation used only to prove the PreProcessor runs before /session/use.
vars.put('PREPARED_TOKEN', token.toUpperCase(Locale.ROOT))
vars.put('CORRELATION_STATE', 'ready')
}
Use Groovy and enable compiled-script caching when scripting is
needed. Do not insert ${CORR_TOKEN} directly into
cached script text; use vars.get('CORR_TOKEN') so
runtime per-thread values are read on every invocation.
11. Read-only inspection before changing correlation
- Inspect the successful response and identify the exact dynamic field and owning sampler.
- Search the JMX for hard-coded captured tokens/IDs and note every downstream reference.
- List each Pre/Post-Processor and its tree parent/scope.
- Check extractor reference name, expression, match number, and default.
- Use a one-thread Debug Sampler to inspect only synthetic correlation variables.
- Compare downstream request metadata and target event logs without persisting real secrets.
-
Record JTL,
jmeter.log, generator headroom, and target behavior before changing load.
12. DevOps connection: correlation is reproducibility
A load plan should work when started later by another developer or CI agent because it discovers dynamic state from the system under test. Hard-coded replay data makes a plan dependent on one historical session. Proper correlation turns the JMX into a repeatable protocol workflow.
Knowledge check
Why should a response-derived session token be a JMeter variable rather than a property?
Variables are thread-local, so each virtual user keeps its own session state; properties are shared by the process and can create cross-thread races.
When does a Post-Processor run relative to assertions?
After the sampler but before assertions, so it cannot see assertion results for that same sample.
Why use CORR_MISSING as the extractor default during development?
It makes a missing match explicit and prevents silent/stale correlation from looking valid.
Why is JSON JMESPath Extractor preferred over Groovy for session.token?
It directly matches the JSON response format, is easier to review, and avoids unnecessary custom scripting.
What is wrong with replaying one recorded token from every thread?
The workload no longer models independent users and may fail or benchmark one shared session instead of concurrent sessions.
Official references and version notes
- Elements of a Test Plan — Pre-Processor/Post-Processor purpose, scope, and execution order: configuration → pre-processors → timers → sampler → post-processors → assertions → listeners.
- Component Reference — Regular Expression, JSON/JMESPath, Boundary, JSR223 Pre/Post Processor, User Parameters, and Result Status Action Handler semantics.
- Functions and Variables — thread-local JMeter variables versus process-wide JMeter properties.
- Best Practices — CLI load execution and scripting/performance guidance.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive behavior was rechecked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK
for labs and no third-party plugins; JMeter 5.6.3 requires Java
8+. Pre-Processors execute before their in-scope sampler and
Post-Processors execute after the sampler but before Assertions.
Processor behavior is scope-driven rather than determined by
visual sibling order. Built-in Post-Processor extractors store
results in JMeter variables, which are normally thread-local. JSON
JMESPath Extractor accepts one JMESPath expression, can select a
match, and can set an explicit default when nothing matches.
Regular Expression and Boundary Extractors likewise support
explicit defaults, which are especially useful during debugging so
a missing extraction is distinguishable from a processor that
never ran. JSR223 Pre/Post Processors provide
vars (thread variables), props (shared
JMeter properties), and the relevant sampler/result context;
Groovy with compiled-script caching is preferred over BeanShell
when scripting is actually necessary. Mandatory labs use a
built-in extractor for correlation and only a tiny Groovy
PreProcessor for a deliberately simple transformation; Chapter 10
covers extractor families in greater depth.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.