JMeter Variables: Giving Every Virtual User a Memory

JMeter Variables: Giving Every Virtual User a Memory

A complete, plain-English guide to how Apache JMeter creates, stores, shares and protects data while your load test runs, from the very first variable to production-grade test design.

Imagine a call centre with five hundred agents answering phones at the same time. Each agent has a notepad. One agent writes down “caller name: Priya”, another writes “order number: 48213”, and a third writes “caller is angry, offer a refund”. Nobody reads anybody else’s notepad. If all five hundred agents shared one single notepad, the names and numbers would be scribbled over each other within seconds and every conversation would turn into chaos.

A JMeter load test is that call centre. Every virtual user, which JMeter calls a thread, behaves like one agent, and a variable is that agent’s notepad entry. Understanding where the notepad lives, who can read it, how long the writing lasts and what happens when two agents need to share a fact is the difference between a load test that tells the truth and one that quietly lies to you. This guide teaches all of it from zero.

1What Is a Variable, and Why Does a Load Test Need One?

A variable is simply a named box that holds a value you want to reuse or change while the test is running.

The problem variables solve

Apache JMeter is a free, open-source tool that sends many requests to a website, API or server at once so you can see how the system behaves under pressure. When you record or build a test, you usually start with fixed values: one username, one product number, one server address. That works for a single request. It breaks the moment you pretend to be a thousand different people.

If a thousand virtual users all log in with the same username, the server may block the account, serve everyone the same cached page, or simply behave unrealistically. Real traffic is varied. Real users have different names, search for different things, and receive different tokens from the server. Variables are how you inject that variety, and how you carry information from one request to the next.

Simple Analogy

Think of a form letter with blanks: “Dear ______, your order ______ has shipped.” The letter template never changes. Only the blanks do. A JMeter variable is a blank in your test, and the value is filled in fresh each time the test needs it.

Three jobs a variable does

Job 1

Hold Configuration

Store things like the server name, port or timeout once, so changing the target environment means editing one place instead of fifty.

Job 2

Supply Test Data

Feed each virtual user different usernames, passwords, search words or product codes, usually from a file.

Job 3

Carry Server Responses

Capture a value the server sent back, such as a session token or order id, and send it in the next request. This is called correlation.

Job 4

Control Test Flow

Decide whether to run a branch, how many times to loop, or when to stop, based on the value a variable currently holds.

i
Plain Words

A variable has two parts: a name you choose, and a value that can change. You use the name wherever you want the value to appear. JMeter swaps the name for the value just before sending the request.

The basic way to use one

Anywhere JMeter lets you type text, such as a URL path, a header, a form field or a body, you can wrap a variable name in a dollar sign and curly braces. For example, writing ${customerId} tells JMeter: “when you build this request, replace this with whatever the variable named customerId holds right now.” Names are case sensitive, so ${customerId} and ${CustomerId} are two different notepad entries.

2Variables Versus Properties: Two Different Notepads

JMeter keeps two separate kinds of stored values, and mixing them up is the number one beginner confusion.

A JMeter variable belongs to one thread, one virtual user. A JMeter property belongs to the whole JMeter process and is visible to every thread at once. Properties usually come from configuration files or from the command line when you launch the test. Variables are created and changed while the test runs.

Simple Analogy

Properties are the notice board in the office lobby: one board, everybody reads it, and it was put up before the work day began. Variables are each person’s desk notepad: private, short-lived and constantly rewritten.

AspectVariableProperty
Who can see itOnly the thread that owns itEvery thread in the JMeter process
Typical originUser Defined Variables, CSV files, extractors, countersjmeter.properties, user.properties, command-line options
How you read itDollar-brace nameThe property function, with an optional default
Changes during the runConstantly, per threadRarely, and only if you deliberately set it
Thread safetySafe, because nobody else touches itNeeds care, because all threads share it
Best usePer-user data and server responsesEnvironment settings and run-wide switches

Why the split exists

Load tests run hundreds or thousands of threads in parallel. If every value were global, one thread’s session token would overwrite another’s within milliseconds. Keeping variables private per thread removes that whole category of bug. Properties stay global because things like “which server are we hitting” or “how many users should run” are the same for everybody and need to be decided once.

!
Common Misconception

Writing a plain variable reference for something you passed on the command line will not work. Command-line values become properties, and you must read them with the property function. The test will run, but the value will show up as a literal unreplaced name.

3Internal Working: Where a Variable Actually Lives

Under the surface, every thread owns a small private map of names to text values.

When JMeter starts a thread, it hands that thread its own object that behaves like a dictionary: names on the left, values on the right. Developers call it the thread’s variable map. Every element that runs inside that thread, whether it is a sampler, a pre-processor, an extractor or a controller, looks names up in that same dictionary. When your test plan says ${orderId}, JMeter performs a lookup in the running thread’s dictionary and substitutes the answer into the text.

Values are text

The dictionary stores text. Even when you extract the number 42 from a response, JMeter stores the characters “4” and “2”. If you want to do arithmetic you need a function or a script that converts text to a number, calculates, and writes the answer back as text. Advanced scripts can also stash whole objects in the map, but beginners should think of every value as a string.

What happens when a name is missing

If a name does not exist in the dictionary, JMeter does not stop with an error. It leaves the original text, braces and all, in the request. That means your server may receive a literal “${orderId}” and respond with an error. This silent failure is why careful testers always check the request body in a results viewer during a trial run.

!
Watch Out

A response of “400 Bad Request” with a strange-looking body is often a variable that never got filled. Always inspect the actual sent request first, before blaming the server.

Isolation between threads

Because each thread has its own dictionary, thread 1 writing “token = abc” cannot disturb thread 2 holding “token = xyz”. When JMeter starts the test, the values defined at the test plan level are copied into each new thread’s dictionary as starting values. After the copy, each thread is free to overwrite its own version without affecting anyone else.

1 map
per virtual user (thread)
Text
default type of every value
Literal
what you get when a name is missing

4Core Concepts: The Many Ways to Create a Variable

JMeter offers a toolbox of elements that produce variables, each designed for a particular situation.

Config Element

User Defined Variables

A simple table of names and fixed values. Perfect for server host, port, protocol and think-time settings.

Config Element

CSV Data Set Config

Reads a text file line by line and loads each column into a variable. The standard way to give users unique logins.

Post-Processor

Extractors

Pull values from a server reply using regular expressions, JSON paths, XPath, CSS selectors or text boundaries.

Config Element

Counter

Produces a number that goes up by a fixed step each time it is used. Ideal for unique ids and sequence numbers.

Config Element

Random Variable

Generates a random number within a range, handy for picking random products or varying input sizes.

Script Element

JSR223 Elements

Lets you create or change variables with a small script when no built-in element does exactly what you need.

Function

Built-in Functions

Generate values on the spot, such as unique ids, timestamps, random strings or property lookups, right where you type them.

Controller

ForEach and Loop Helpers

Create loop index variables and walk through lists of values that extractors have collected.

Simple Analogy

Think of a restaurant kitchen. Some ingredients are bought in bulk and never change (flour: user defined variables). Some arrive in a fresh delivery for each order (the CSV file). Some are made on the spot from what the customer just ordered (extractors). A kitchen needs all three kinds.

How to choose

Ask one question: where does the value come from? If you decide it before the test, use a user defined variable or a property. If a file supplies it, use CSV Data Set Config. If the server hands it to you, use an extractor. If it should be invented fresh each time, use a counter, a random variable or a function. Choosing by origin keeps your plan easy to read and easy to maintain.

5User Defined Variables in Depth

The simplest variable source is also the one with the most surprising behaviour, so it deserves a careful look.

What it does

User Defined Variables is a table with two columns: a name and a value. You list your settings once, for example the web server host name, the port, a default pause time, or the base path of the application. Everywhere else in the plan you refer to the names. When you later move the test from a staging server to a pre-production server, you change one cell instead of editing dozens of requests.

The surprising rule about timing

Many newcomers assume a User Defined Variables element runs at the moment the test reaches it, like a sampler. It does not. JMeter processes all User Defined Variables elements at the very start of the test, in the order they appear, regardless of where you placed them in the tree. Placing one halfway down a thread group therefore does not make it “activate” halfway through.

i
Good to Know

Because the elements are processed in order at the start, a later element can use values defined by an earlier one. This lets you build a full address from a protocol, host and port that you defined above it.

When the value must change mid-test

Because of that start-of-test behaviour, User Defined Variables is the wrong tool for values that need to change while the test runs. For those, use a post-processor, a counter, a function or a small script. A related but different element, the User Parameters pre-processor, assigns values per thread and per iteration at the moment it is reached, which suits “each user gets a different value in this step” scenarios.

ElementWhen values are setBest for
User Defined VariablesOnce, at test startFixed environment settings
User ParametersWhen reached, each timePer-user values inside a flow
CSV Data Set ConfigEach iteration, per threadUnique logins and test data
ExtractorsAfter a response arrivesTokens and ids from the server

6Scope and Execution Order: Who Sees What, and When

Two rules decide whether a variable exists when you need it: where its element sits in the tree, and the fixed order JMeter runs elements in.

Scope rules

A test plan is a tree. Elements apply to the branch beneath their parent. A configuration element placed directly under a thread group affects every sampler in that group. Placed under a single sampler, it affects only that sampler. Placed at test plan level, it affects everything. The same rule applies to extractors: an extractor sitting under one sampler reads only that sampler’s response, while one placed at thread group level tries to read every sampler’s response, which is rarely what you want.

Execution order inside one step

For every sampler, JMeter follows the same sequence, regardless of how you arranged the elements visually.

1

Configuration elements

Defaults and data sources are applied first so values are ready.

2

Pre-processors

Run just before the sampler. Good for preparing a value this request needs.

3

Timers

Add the pause before the sampler fires.

4

The sampler itself

Variables inside the request are replaced and the request is sent.

5

Post-processors

Extractors read the response and write new variables for later steps.

6

Assertions and listeners

Check the result and record it.

!
Classic Mistake

Using a value in the same request that produces it. An extractor runs after its sampler, so the variable cannot exist yet when that same request is built. The value is only available from the next request onward.

“A variable can only be used after the step that creates it has finished.”

7CSV Data Set Config: Feeding Each User Different Data

The CSV Data Set Config element is the workhorse for realistic, varied load.

CSV stands for comma separated values, a plain text file where each line is a record and a delimiter splits the columns. You list the file’s columns as variable names, and JMeter reads one line per thread per iteration, loading each column into the matching variable. Thread one may get “alice” and her password, thread two gets “bob” and his, and so on down the file.

The settings that matter

Setting

Variable Names

Comma-separated names that map to the columns in the file, in the same order.

Setting

Ignore First Line

Skips a header row so column titles are not treated as data.

Setting

Delimiter

The character that separates columns. A comma is common, but tabs or semicolons are fine.

Setting

Recycle on EOF

When the file ends, start again from the top instead of running out of data.

Setting

Stop Thread on EOF

End the thread when the data runs out, useful when each record may only be used once.

Setting

Sharing Mode

Decides whether all threads share one reading position, each group has its own, or each thread has its own.

Sharing mode, explained with a deck of cards

Simple Analogy

Picture a deck of cards. If every player draws from one shared deck, nobody gets the same card twice. If each table has its own deck, cards repeat across tables. If every player holds a private deck, everyone sees the whole sequence from the first card. These three situations match the “all threads”, “current thread group” and “current thread” sharing modes.

Unique logins, no repeats

Use the default all-threads sharing, turn off recycling and enable stop thread at end of file. Each account is used exactly once, which suits systems that lock an account after a duplicate login.

Endless realistic browsing

Use recycling so a small list of search terms or product codes repeats for as long as the test runs, giving a long steady load without needing millions of rows.

i
Practical Tip

When you run the test across several machines, each machine reads its own copy of the file. If every copy contains the same rows, users on different machines will collide. Split the data so each machine has a different slice.

8Extractors and Correlation: Capturing What the Server Says

Correlation means taking a value from one response and using it in a later request. Extractors are the tools that do the capturing.

Modern applications constantly hand out one-time values: session tokens, anti-forgery codes, order numbers, cart identifiers. If you record a test and replay it exactly, the old token is stale and the server rejects it. The fix is to capture the fresh token from the earlier response, place it in a variable, and reference that variable in the next request. This habit is called correlation, and it is the single most important variable skill for realistic tests.

Simple Analogy

At a cloakroom, you hand over your coat and receive a numbered ticket. Tomorrow’s ticket has a different number. You cannot print tickets in advance. You must read the number you were just given and show that exact one later. An extractor is you reading the ticket number; the variable is your hand holding it.

The family of extractors

Pattern

Regular Expression Extractor

Matches text with a pattern. Powerful and universal, but patterns can be fragile if the page layout changes.

Structured

JSON Extractor

Follows a path through a JSON reply. The cleanest choice for modern APIs.

Simple

Boundary Extractor

Captures whatever sits between a left and a right marker. Easy to read and hard to get wrong.

HTML

CSS Selector Extractor

Picks elements out of a web page using the same selector language browsers use.

XML

XPath Extractor

Navigates XML replies, common with older enterprise services.

Settings every extractor shares

Each extractor asks for a reference name, which is the variable it will create, a match number that says which hit to keep, and a default value. The match number can mean the first hit, the Nth hit, a random hit, or every hit. The default value is what the variable holds if nothing matched.

i
Smart Habit

Always set a distinctive default such as “TOKEN_NOT_FOUND”. Without a default, a failed extraction leaves the variable holding an older value, and the test may keep running as though everything were fine. With a distinctive default, a simple assertion can flag the failure instantly.

When there are many matches

If you ask an extractor for all matches, JMeter creates a family of variables rather than one. Using a reference name of “product” as an example, you receive a count variable that says how many hits were found, plus numbered variables for the first, second, third match and so on. This family is what a ForEach controller later walks through, one value per loop pass.

Match numberMeaningTypical use
1The first hitA single token or id
2, 3, …The Nth hitSkipping known earlier matches
0A random hitPicking a random product from a list
-1All hits, as a numbered familyLooping through every result

9Counters, Random Values and On-the-Spot Generators

Sometimes the data must be invented fresh, and JMeter has several ways to do it.

The Counter

A Counter produces a number starting at a value you choose and increasing by a step each time it is used. You can cap it at a maximum, format it with leading zeros, and, importantly, decide whether each virtual user has its own private count or whether all users share one running total. Shared counting gives globally unique numbers such as invoice ids. Private counting gives each user a personal sequence such as “my third item”.

Simple Analogy

A shared counter is the ticket dispenser at a bakery: everyone takes the next number, and no number repeats. A private counter is each customer counting their own purchases on their fingers.

Random Variable

The Random Variable element picks a number between a minimum and a maximum and stores it. It can also keep a per-user seed so that test runs are repeatable. Use it to choose a random page number, a random quantity, or a random index into a list of products.

Functions that generate values inline

Function

UUID

Produces a long unique identifier, ideal for correlation ids and idempotency keys.

Function

Time

Returns the current moment in a format you choose. Handy for timestamps and date fields.

Function

RandomString

Builds a text of chosen length from chosen characters, perfect for unique emails or names.

Function

ThreadNum

Returns the number of the current virtual user, useful for building user-specific data without a file.

!
Careful

A function typed straight into a request is re-evaluated every time that field is built. If you need the same generated value in three places, generate it once into a variable first and reference the variable.

10Properties, Functions and Passing Values Around

Properties make a test configurable from the outside, and a few functions bridge the gap between the two notepads.

Where properties come from

JMeter loads properties from its main configuration file, then from a user-level override file, and finally from options typed on the command line. Later sources win over earlier ones. The command-line option that sets a local property is the dash-J option, and the one that also pushes a property to remote test machines is the dash-G option. Both let one test file run at many sizes without ever being edited.

SourceScopeTypical use
jmeter.propertiesWhole installationGlobal engine defaults
user.propertiesYour installation, overriding defaultsPersonal or team settings
Dash-J optionOne run, local machineUsers, duration, environment name
Dash-G optionOne run, all remote machinesDistributed run settings

Reading and writing across the boundary

The property function reads a property and lets you supply a fallback value if it is missing. For example, asking for a property named “users” with a default of 10 means “use whatever was passed in, otherwise ten”. This pattern makes a plan safe to open and run by double-click while still being steerable from automation. A companion function writes a property, which is how one thread group can leave a value that a different thread group reads later.

One plan, three environments

Pass the environment host name as a property from the build pipeline. The same file hits development, staging or pre-production depending only on the command that launches it.

Login once, share the token

A setup thread group logs in and writes the token as a property. Test thread groups read that property so thousands of users reuse one authenticated session where that is appropriate for the scenario.

Variables whose names are themselves variables

A helper function can resolve a name that is built from other variables. If you have variables named for a row and a column, you can assemble the name on the fly and fetch the value it points to. It is powerful for looping through tables, but it is also the point where plans become hard to read, so use it sparingly and comment it well.

i
Rule of Thumb

Properties describe the run. Variables describe the user. If a value is the same for everybody, make it a property. If it differs per person, make it a variable.

11Scripting with Variables: JSR223 Elements

When the built-in elements are not enough, a short script can read, calculate and write variables freely.

JSR223 elements run small scripts in a language such as Groovy. Inside the script, JMeter provides a ready-made handle to the current thread’s variable dictionary, so the script can read any variable, change it, create a new one, or remove it. The community recommendation is to prefer Groovy with script caching enabled, because it is compiled once and runs fast, while older scripting options are slower under heavy load.

Good reasons to script

Use

Calculations

Add up prices from a response, compute a total, or work out a date offset.

Use

Conditional Logic

Choose one of several values depending on what a previous response contained.

Use

Signing and Hashing

Produce a signature or checksum a request needs, then store it for use in a header.

Use

Cleaning Data

Trim, convert or reformat an extracted value before it is used again.

!
Performance Warning

Scripts execute inside every virtual user, every iteration. A slow script steals processor time from the load generator, which makes your results look worse than the system really is. Keep scripts short, enable caching, and never do slow work such as large file reads inside them.

The golden rule of scripting

Script only when no ready-made element can do the job. Built-in elements are clearer to colleagues, easier to debug, and usually faster. A plan that is ninety percent standard elements and ten percent careful scripting ages far better than one that is mostly custom code.

12Data Flow and Lifecycle of a Variable

Follow a single value from the moment it is born to the moment it disappears.

A variable has a life story. It is created by some element, it lives in its thread’s dictionary, it is read by later elements, possibly overwritten, and finally it vanishes when the thread ends. The diagram below shows the common path of a login token, the most typical example.

sequenceDiagram
    participant U as Virtual User Thread
    participant D as Thread Variable Map
    participant S as Server
    U->>S: Send login request
    S-->>U: Reply containing a session token
    U->>D: Extractor stores token under a chosen name
    U->>D: Next request looks the name up
    D-->>U: Returns the stored token text
    U->>S: Send request carrying the token
    S-->>U: Reply for the authenticated user
        
FIG 1 — A token is captured after one response and reused in the next request

The phases of a variable’s life

1

Birth

Test start copies shared defaults into each thread, or an element writes a fresh value during the run.

2

Use

Later elements read it, and JMeter substitutes its value into requests.

3

Change

A new iteration, a new extraction or a script overwrites the value with a newer one.

4

Death

When the thread finishes, its entire dictionary is discarded. Nothing carries over to the next run.

i
Iteration Reminder

A thread usually repeats its steps many times. Variables from the previous loop are still present at the start of the next one unless something overwrites them. A stale value surviving into the next iteration is a frequent cause of confusing results.

13Architecture: Variables in Distributed Testing

When one machine cannot generate enough load, several machines share the work, and variables must be planned accordingly.

In distributed mode, one controller machine sends the test plan to several worker machines, often called remote engines or load generators. Each worker runs its own copy of JMeter and its own set of threads. Variables stay private to each thread on each worker, exactly as before. What changes is everything that lives outside JMeter’s memory, especially data files and properties.

flowchart TD
    C["Controller machine: holds the test plan"] --> W1["Worker 1: own threads and own variable maps"]
    C --> W2["Worker 2: own threads and own variable maps"]
    C --> W3["Worker 3: own threads and own variable maps"]
    W1 --> T["System under test"]
    W2 --> T
    W3 --> T
    F1["Data file copy 1"] --> W1
    F2["Data file copy 2"] --> W2
    F3["Data file copy 3"] --> W3
        
FIG 2 — Each worker has private variables and reads its own copy of the data file

What to plan for

Data

Files Are Not Shared

Every worker reads its local file. Split the data so workers use different rows, or duplicate users will appear.

Properties

Push Settings With Dash-G

Use the global option so all workers receive the same settings, rather than only the controller.

Counters

Counts Are Per Worker

A “global” counter is only global on its own machine. Offset each worker’s start value to keep numbers unique.

Properties

Set Values Do Not Travel

A property written by a thread on one worker is not visible on another worker.

14Security: Handling Secrets and Sensitive Data

Load tests often touch real systems, so passwords and tokens in variables deserve respect.

Test plan files are plain text. It is tempting to type a real password into a User Defined Variables table because it is quick. That file then travels through email, chat, version control and shared drives. Treat any credential inside a plan as if it were published.

Safer Habits

  • Use dedicated test accounts that have no real data and limited rights
  • Pass secrets as properties from your pipeline’s secret store at run time
  • Keep credential files out of version control
  • Delete or rotate test credentials when the test campaign ends
  • Restrict who can read result files

Risky Habits

  • Typing production passwords into the plan itself
  • Saving every variable into result files without checking what they contain
  • Leaving debug output enabled while tokens are printed to the log
  • Reusing one powerful admin account for all threads
!
Hidden Leak

JMeter can be configured to write chosen variables into the result file. This is useful for debugging, but it also writes tokens and personal data into a plain file. Enable it deliberately, for specific variables only, and clean the files afterwards.

i
Command-Line Caveat

Values passed on the command line can be visible to other users on the same machine through the process list. On shared build servers, prefer a file or the pipeline’s masked-secret mechanism over a bare option.

15Monitoring, Logging and Debugging Variables

You cannot fix what you cannot see, and variables are invisible by default.

Because a missing variable fails silently, every serious tester builds a habit of looking inside the running test. JMeter gives you several windows into the thread’s dictionary, and learning to use them early saves hours of confusion later.

Your inspection toolkit

Sampler

Debug Sampler

Adds a result row that lists every current variable, property and system value. Place it after an extraction to confirm the value arrived.

Post-Processor

Debug PostProcessor

Records variable contents alongside each sampler’s result without adding a separate request.

Listener

View Results Tree

Shows the exact request that was sent, so you can spot an unreplaced name at a glance.

Listener

Log Viewer

Surfaces warnings, such as an extractor that could not find its pattern or a function that failed.

A reliable debugging routine

1

Run with one user and one loop

Remove load. The goal is to see the logic clearly, not to stress the server.

2

Read the sent request

Check whether any request still contains a literal name with braces.

3

Check the previous response

If a value was not captured, compare the response body with your pattern or path.

4

Inspect with a debug element

Confirm the variable’s exact spelling, case and current value.

5

Remove debug elements before the real run

Debug output and heavy listeners consume memory and distort results under load.

i
Trust but Verify

Add assertions that check the extracted variable is not your “not found” default. A failing assertion turns a silent data problem into a visible, countable error.

!
Never Do This Under Load

Do not leave the View Results Tree enabled in a large run. It stores every request and response in memory and can slow or crash the load generator.

16Design Patterns That Work

Experienced testers keep returning to a handful of proven ways to organise variables.

Pattern: Environment Block at the Top

Gather host, port, protocol and base path into a single User Defined Variables element at the test plan level, each backed by a property with a default. Everything else references these names. Switching environments becomes a one-line change on the command line.

Pattern: Setup Once, Reuse Everywhere

Use a setup thread group for expensive one-time work such as creating a shared reference record, then publish the result as a property for the main thread groups to read.

Pattern: Extract Close to the Source

Place each extractor beneath the single sampler whose response it reads. This keeps scope tight, prevents accidental matches in other responses, and makes the plan self-explanatory.

Pattern: Fail Loudly on Missing Data

Give every extractor a distinctive default and follow it with an assertion. The test then reports a clear error at the exact step where correlation broke.

Pattern: Naming Convention

Name variables with a consistent prefix that hints at their origin, such as one prefix for configuration values, another for file data and another for extracted values. A reader can then tell where a value came from just by reading its name.

Pattern: Reset Between Iterations

At the start of each loop, clear or reinitialise variables that should not carry over, so one iteration’s data cannot leak into the next.

flowchart LR
    P["Run-wide settings: properties"] --> E["Environment block: user defined variables"]
    D["Test data file"] --> V["Per-user variables"]
    E --> V
    R["Server responses"] --> X["Extractors"]
    X --> V
    V --> Q["Requests built with substituted values"]
    Q --> R
        
FIG 3 — Three sources feed the per-user variables, which in turn shape every request

17Anti-Patterns to Avoid

These habits look harmless on day one and cause expensive confusion by week three.

ANTI-PATTERN-01 Avoid
Problem

Hard-coding the same literal value, such as a server name or a token, directly into dozens of requests.

Why It’s Harmful

A single change requires dozens of edits, and one missed edit sends traffic to the wrong place. Recorded tokens also expire, so the test fails the next day.

Correct Approach

Define configuration once as a variable or property, and capture dynamic values with extractors.

ANTI-PATTERN-02 Avoid
Problem

Using a shared property to hold per-user data, for example storing each user’s session token in one global property.

Why It’s Harmful

All threads read and write the same slot, so users receive each other’s tokens. Results become meaningless and failures appear random.

Correct Approach

Keep per-user values in variables. Use properties only for values that are truly identical for everybody.

ANTI-PATTERN-03 Avoid
Problem

Placing a User Defined Variables element mid-plan and expecting it to take effect at that point.

Why It’s Harmful

It is processed at test start, so values you expected to change later never do. The mismatch between expectation and behaviour wastes debugging time.

Correct Approach

Use a pre-processor, counter, function or script for values that must change during the run.

ANTI-PATTERN-04 Avoid
Problem

Storing entire response bodies in variables “just in case”.

Why It’s Harmful

Every thread holds its own copy. With a thousand threads and a large page each, memory fills quickly and the load generator slows down.

Correct Approach

Extract only the small values you actually need and let the rest be discarded.

ANTI-PATTERN-05 Avoid
Problem

Leaving an extractor with an empty default so a failed match keeps the old value or produces nothing.

Why It’s Harmful

The test continues with stale or blank data and reports success or misleading failures, hiding the true cause.

Correct Approach

Set an obvious default and assert against it so failures are loud and precise.

18Best Practices and Common Mistakes

A short checklist of habits that separate dependable tests from fragile ones.

Do This

  • Choose the variable source by asking where the value originates
  • Give every variable a clear, consistent, case-correct name
  • Verify with a single-user trial run before scaling up
  • Limit extractor scope to the one sampler that needs it
  • Use properties with defaults so the plan always runs standalone
  • Document unusual variables with a short comment in the plan

Not This

  • Misspelling a name or changing its capitalisation between uses
  • Expecting a value in the same request that creates it
  • Sharing recycled test data in a way that causes duplicate users
  • Running with heavy listeners and debug elements enabled
  • Embedding credentials and then sharing the plan file
  • Writing long scripts where a built-in element exists

The mistakes that cost the most time

Mistake 1

Wrong Reading Method

Reading a command-line value as if it were a variable. It is a property and needs the property function.

Mistake 2

Regex Too Greedy

A loose pattern captures far more text than intended. Tighten the markers around the value.

Mistake 3

Hidden Whitespace

Trailing spaces or line breaks in a data file make a value fail to match on the server.

Mistake 4

Header Row as Data

Forgetting to skip the first line makes the first user log in as a column title.

Mistake 5

Stale Values Across Loops

Reusing last iteration’s value because nothing reset it before the next pass.

Mistake 6

Encoding Surprises

A special character in a value, such as an ampersand or plus sign, breaks the request unless it is encoded properly.

19Real-World and Industry Examples

Variables show up in every kind of performance test. Here are the situations you will meet most often.

Online Shop Flash Sale

Thousands of shoppers log in with unique accounts from a data file, search random product codes from a recycled list, capture the cart identifier after adding an item, and use it in the checkout request. Variables tie the whole journey together.

Banking API Test

Each virtual customer authenticates, receives a short-lived access token, stores it, and attaches it to every later call. Because tokens expire, a counter-driven refresh step renews the token on a schedule, and a unique request identifier is generated for every payment.

Ticket Booking Peak

Events with limited seats require each user to choose a different seat. A random variable picks a seat from the available list extracted from an earlier response, so users do not all fight for seat number one.

Continuous Delivery Pipeline

A short load test runs after every build. The pipeline passes the environment host, user count and duration as properties, so the same plan acts as a quick smoke test on weekdays and a full soak test overnight.

Microservices Workflow

A request to one service returns an order identifier, which the next service needs, which in turn returns a shipment identifier. Chained extractors pass each identifier forward like a relay baton.

Tokens
captured and reused per user
Accounts
fed from data files
Settings
driven by run-wide properties

20Advantages, Disadvantages and Trade-offs

The variable system is flexible, but flexibility has a price.

Advantages

  • One plan can simulate thousands of different, realistic users
  • Per-thread privacy removes a whole class of data-collision bugs
  • Configuration is changed in one place instead of many
  • Correlation lets tests survive dynamic, one-time server values
  • Properties let automation steer the same plan at any size
  • Many built-in elements mean most needs require no scripting

Disadvantages / Trade-offs

  • A missing name fails silently, leaving a literal placeholder
  • Values are text, so calculations need extra care
  • Each thread’s private copy costs memory at high thread counts
  • Execution order and scope rules surprise newcomers
  • Distributed runs need extra planning for files and counters

Choosing between neighbours

If you need…PreferBecause
A fixed setting used everywhereUser Defined Variables backed by a propertySimple, central, overridable
Different data per userCSV Data Set ConfigRealistic variety with no scripting
A one-time value from the serverAn extractorCaptures fresh values after each reply
A globally unique numberA shared counter or UUID functionNo two users collide
Custom calculationA short cached scriptFlexible when nothing else fits
Passing a value between thread groupsA propertyVariables are private to each thread

21Variables Inside Controllers: Decisions and Loops

Controllers decide what runs and how often, and variables are the information they use to decide.

A controller is a JMeter element that organises samplers into logic: run this group, repeat that group, skip this branch. Many controllers accept variables, which turns a fixed script into a flexible, data-driven one. The test then behaves differently for different users, depending on what the server told them or what their data file contained.

Decision

If Controller

Runs its children only when a condition is true. The condition usually compares a variable to a value, such as “was the account type premium”.

Loop

Loop Controller

Repeats its children a set number of times. The count itself can come from a variable or a property, so loop depth is adjustable from outside.

Loop

ForEach Controller

Walks through a numbered family of variables, such as the all-matches output of an extractor, using each value in turn.

Loop

While Controller

Keeps repeating while a condition holds, for example “keep polling until the job status variable says finished”.

Decision

Switch Controller

Chooses exactly one child based on a variable’s value or position, perfect for simulating a weighted mix of user actions.

Group

Transaction Controller

Bundles several requests into one timed business step such as “complete checkout”, reported as a single result.

Simple Analogy

A controller is a traffic officer at a junction and variables are the signs drivers carry. The officer reads the sign and waves one car left, another right, and a third around the block three times.

Polling with a variable

Many systems process work in the background. You submit a request, receive a job identifier, and then must ask repeatedly whether the job is done. A while-style loop combined with an extractor that refreshes a status variable models this perfectly. Always add a short timer inside the loop and a sensible upper limit on attempts, or a stuck job will make a virtual user spin forever and flood the server.

!
Infinite Loop Danger

A loop that depends on a variable can run forever if that variable never changes, for instance when the extractor silently failed and kept an old value. Protect every polling loop with a maximum attempt count.

Condition text is still just text

Because variables hold text, comparing numbers inside a condition needs attention. The text “10” and the text “9” sort differently from the numbers ten and nine. Use the condition style that treats the expression as a script, or convert deliberately, when you compare quantities rather than words.

22Performance and Memory: What Variables Cost

Variables are cheap one at a time, but a big test multiplies every small cost by thousands of threads.

The load generator is itself a program with limited memory and processor time. If it becomes the bottleneck, your measurements describe the generator, not the system you meant to test. Variables influence that cost in three ways: how much text they store, how often they are written, and how complex the elements that fill them are.

Threads × Size
memory used by variables grows with both
Per iteration
how often extractors and scripts run
Generator first
keep the tool from becoming the bottleneck

Where the cost hides

Large extracted values

Capturing a whole page or a big JSON array into a variable duplicates it in every thread. Capture only the small fragment you will actually reuse.

Greedy or complicated patterns

A regular expression that scans a huge response with a vague pattern can burn more processor time than the request itself. Anchor patterns with clear surrounding markers, or choose the JSON or boundary extractor, which are simpler and faster for structured replies.

Scripts without caching

Uncached scripts are recompiled repeatedly. Turn caching on for script elements and keep their bodies short.

Many all-match families

Asking for every match when you only need one creates dozens of variables per response, per user. Request the single hit you need.

Listeners that record variables

Saving many variables into result files increases file size and write time. Record only what a specific investigation requires.

How to verify the generator is healthy

Watch the load generator’s own processor and memory during a trial run. If either stays high while server response times look suspiciously flat, suspect the generator. Reducing stored data, simplifying patterns and removing debug listeners usually restores headroom. When one machine still cannot cope, spread the threads across several machines as described in the distributed chapter.

“Measure the system under test, not the tool that is testing it.”

23Built-in Values You Get for Free

JMeter quietly provides several useful values so you do not have to invent them.

Without any setup, a running test already has access to a few handy pieces of information. Some are properties that record when the test began, in several formats, which is handy for stamping data with a run date. One is a per-thread variable that reports whether the previous sampler succeeded. Others are available through functions, such as the number of the current thread or the current moment in time.

What you want to knowWhere it comes fromTypical use
Did the last request succeed?A built-in per-thread variableBranch to a recovery step only after a failure
Which virtual user is this?The thread number functionBuild unique names without a data file
When did the test start?Built-in start-time propertiesLabel created records with the run date
What is the time right now?The time functionTimestamps and date fields in requests
Which loop pass is this?Iteration information available to scriptsBehave differently on the first pass
i
Use Them to Make Data Unique

Combining the thread number with a counter or a timestamp gives each user a different, predictable name such as a user label plus a number. That is often enough to avoid duplicate-record errors without managing any data file.

Recovering from a failed step

Because the previous-sample result is a variable, an If Controller can read it and run recovery logic only when something went wrong, for example re-authenticating after an unexpected rejection. This lets a long scenario survive occasional errors rather than collapsing, while still recording every failure for your report.

!
Do Not Hide Problems

Recovery logic should keep the test moving, never make failures invisible. Keep assertions and error recording active so that retries still appear in your results.

24An End-to-End Walkthrough in Plain Words

Let us build, in sentences rather than code, a realistic test for an online bookstore.

Our goal is to simulate customers who log in, search for a book, add it to a cart and pay. The test must work against any environment and handle one thousand different customers. We will see every kind of variable doing its job.

1

Decide the run-wide settings

The environment name, number of users and duration arrive as properties from the command that starts the test. Each has a sensible default so the plan also runs by double-click.

2

Build the environment block

A single User Defined Variables element turns those properties into host, port and base path names that every request uses.

3

Load customer data

A CSV Data Set Config supplies a unique email and password for each thread, with recycling off so no account is used twice.

4

Log in and capture the token

After the login request, a JSON extractor stores the access token in a variable with a loud default. An assertion fails the step if the default appears.

5

Search and pick a random result

The search reply is extracted with a random match, so each customer picks a different book. The chosen book id lands in a variable.

6

Add to cart and capture the cart id

The add request uses the book id and the token. Its reply provides a cart identifier, which another extractor stores.

7

Pay with a unique reference

The payment request uses the cart id and a freshly generated unique reference, so repeated payments are never rejected as duplicates.

8

Verify on a single user first

Run one user, check each sent request for unreplaced names, then scale up to the full thousand.

flowchart TD
    A["Properties from command line"] --> B["Environment block"]
    C["Customer data file"] --> D["Login request"]
    B --> D
    D --> E["Extract token"]
    E --> F["Search request"]
    F --> G["Extract random book id"]
    G --> H["Add to cart request"]
    H --> I["Extract cart id"]
    I --> J["Payment request with unique reference"]
        
FIG 4 — Every arrow after the login carries a variable captured from the previous reply

Notice the pattern: values flow forward like a relay baton. Properties and files feed the start; extractors keep the baton moving; assertions confirm nobody dropped it. That simple rhythm is the heart of almost every professional JMeter test.

25Keeping Variables Maintainable in a Team

A plan that only its author understands is a liability. A few team habits keep variables readable for years.

Naming

Agree a Convention

Decide together how to name variables from each origin, and write the rule at the top of the plan so newcomers follow it.

Docs

Document Odd Ones

Use the comment field on elements to explain why a variable exists, especially anything created by a script.

Review

Peer-Review Plans

Treat a test plan like code. A second pair of eyes catches misspelled names and stray secrets before they cause trouble.

Version

Use Version Control

Store plans and data templates in version control, but keep real credentials and large data files elsewhere.

Reuse

Share Fragments

Reusable pieces such as a standard login step keep variable behaviour identical across projects.

Clean

Prune Unused Names

Delete variables nothing references. Dead names mislead readers into thinking they matter.

Simple Analogy

A well-kept test plan is like a well-kept kitchen: every jar is labelled, spices live where you expect them, and the next cook can start working without asking a single question.

i
Final Review Question

Before sharing a plan, ask a colleague to open it cold and explain where each variable comes from. If they hesitate, add a comment or rename it.

26The Regular Expression Extractor, Step by Step

The most flexible extractor is also the one that causes the most trouble, so it is worth learning its settings one by one.

A regular expression, often shortened to regex, is a compact description of a text pattern. Instead of saying “find the exact word token”, you can say “find the text that follows the word token, up to the next quotation mark”. The Regular Expression Extractor applies such a pattern to a server response and stores whatever your pattern captured. Parentheses inside the pattern mark the part you want to keep; everything outside the parentheses is only there to locate it.

The settings you will fill in

Where

Field to Check

Choose which part of the reply to search: the body, the headers, the URL, the response code or the response message. Session ids often hide in headers, not in the body.

Name

Reference Name

The name of the variable that will hold your result. Pick something descriptive that tells a colleague what it carries.

Pattern

Regular Expression

The pattern itself. Parentheses mark the capture group, the slice of text that is saved.

Output

Template

Says which group to save. The first group is written as one wrapped in dollar signs; combining several groups lets you glue pieces together into one value.

Choice

Match Number

Which hit to keep: first, a numbered one, a random one, or all of them as a numbered family.

Safety

Default Value

What the variable holds when nothing matched. Make it loud and obvious.

Greedy versus lazy matching

The biggest source of regex bugs is greediness. A greedy pattern grabs as much text as it possibly can, which means it may run past the end of the value you wanted and swallow half the page. A lazy pattern stops at the first opportunity. When you write a capture group, prefer the lazy form that means “as little as possible until the next marker”. Test the result: if your variable contains far more text than expected, greediness is almost always the reason.

Simple Analogy

Imagine telling a helper to “copy everything until you see a full stop”. A greedy helper keeps reading to the last full stop in the whole book. A lazy helper stops at the first one. For capturing small values, you want the lazy helper.

Make patterns resilient

Sturdy Patterns

  • Anchor on stable nearby words, such as a field name that never changes
  • Capture only the value, never surrounding punctuation
  • Allow for optional spaces around separators
  • Keep the pattern short and readable
  • Test it against more than one real response

Brittle Patterns

  • Depending on exact position or line numbers in the page
  • Matching long chunks of layout that designers may change
  • Using a catch-all pattern that matches almost anything
  • Forgetting that quotation marks may be escaped in the reply
  • Never testing what happens when the value is absent
i
When Not to Use Regex

If the response is clean JSON, use the JSON extractor. If the value sits between two simple markers, use the boundary extractor. Regex is the last resort for messy, irregular text, not the first tool to reach for.

27Finding Values That Need Correlation

Before you can capture a dynamic value, you must first discover which values are dynamic.

Beginners often record a scenario, replay it, and watch it fail with no clue why. The usual cause is a hidden dynamic value: something the server generated during recording that is different every time. Finding these values is a detective job with a reliable method.

1

Record the same journey twice

Use different test data each time if possible. Both recordings should follow exactly the same steps.

2

Compare the two recordings

Look at the requests side by side. Any value that differs between the recordings, but is not test data you chose, is a candidate for correlation.

3

Find where each value first appears

Search the earlier responses for the differing value. The first response that contains it is the one where an extractor belongs.

4

Add an extractor and replace the hard-coded value

Place the extractor under the sampler whose reply holds the value, then swap the fixed text in later requests for the variable name.

5

Replay and repeat

Run one user. If a request still fails, there is probably another dynamic value further along. Repeat until the whole journey passes.

Typical dynamic values

Common

Session Identifiers

Handed out after login and required on every later request, either in a header or in the address.

Common

Anti-Forgery Tokens

Hidden form fields that prove the form came from the real site. They change on every page load.

Common

Record Identifiers

Ids of newly created orders, carts or users that later steps must refer to.

Common

Timestamps and Nonces

Single-use numbers and times the server expects to be fresh and unique.

Common

State Fields

Hidden view-state style fields that web frameworks use to remember page state between submissions.

Common

Redirect Targets

Addresses that include generated parts, which a later request must follow exactly.

!
Watch for Partial Matches

A value may appear in more than one place with slightly different encoding. If a captured value works in one request but fails in another, check whether the second place expects it encoded or in a different format.

28Variables in Web Requests: Cookies, Headers and Encoding

Most variables end up inside an HTTP request, and each part of a request has its own habits.

Cookies

The HTTP Cookie Manager acts like a browser’s cookie jar for each virtual user. Cookies that the server sets are stored automatically and sent back on later requests to the same site, so ordinary session cookies often need no variable at all. Each thread has its own jar, which is exactly what you want. Decide whether the jar should be emptied at the start of every iteration. If you leave it full, a returning user is simulated; if you empty it, every loop pass looks like a brand-new visitor.

Headers

The HTTP Header Manager lets you set headers once and have them apply to every request beneath it. Header values can contain variable names, which is how an authorization token captured at login ends up attached to every later call. Place the header manager at thread group level so it covers all requests, but remember the token must already exist by the time the first request that needs it is built.

Parameters and bodies

Form fields, query parameters and request bodies all accept variable names. For JSON bodies, you are writing text that happens to look like JSON, so a missing or empty variable can quietly produce invalid JSON, for instance an unclosed quotation mark. Check the sent body whenever a request is unexpectedly rejected.

Server defaults

The HTTP Request Defaults element stores the protocol, host name and port once. Combined with variables, one element controls where every request goes, so moving a test between environments needs a single change.

Request partWhere variables usually goCommon pitfall
Address and portHTTP Request DefaultsOverriding it inside individual requests
HeadersHTTP Header ManagerUsing a token before it has been extracted
CookiesHTTP Cookie ManagerSharing or clearing cookies by mistake
Form or query fieldsThe request’s parameter tableForgetting to encode special characters
Request bodyThe body text areaProducing malformed JSON from an empty variable
i
Encoding Matters

Characters such as spaces, ampersands, plus signs and equals signs have special meaning in addresses and form data. If a variable may contain them, make sure the parameter is encoded, or the server will read a different value than the one you sent.

29Test Data Strategy: How Much, How Fresh, How Clean

Good variables are only as good as the data behind them, and test data needs its own plan.

A load test is usually limited not by the tool but by the data. Unique accounts, product records and reference numbers must exist in the system before the test starts, and they must still be valid when it ends. Teams that skip this planning step often discover mid-test that they only have two hundred accounts for a thousand users.

Sizing your data

Start with a simple calculation. Count the virtual users, multiply by how many times each will repeat the scenario, and decide whether each repetition may reuse a record. If records must be unique, that product is the number of rows you need. If reuse is acceptable, you only need enough rows to avoid unrealistic hot spots, where thousands of users hammer one record and distort caching.

Read-only data

Search terms, product lists and public pages can be reused freely. A recycled file with a few hundred varied entries is usually enough to look realistic.

Consumable data

One-time gift codes, single-use tickets and unique emails are used up. Each needs its own row, with recycling off and a clear stop condition.

State-changing data

Accounts whose balance or state changes during the test must be reset between runs, or the second run behaves differently from the first.

Preparing and cleaning

1

Generate or load the records

Create the test accounts and records in the target environment using a repeatable procedure, not by hand.

2

Export the file

Produce the data file the test reads, with a clean header row, consistent delimiters and no hidden trailing spaces.

3

Validate a sample

Run a handful of users to prove the accounts work before committing to the long test.

4

Run the test

Use sharing and recycling settings that match the data’s nature.

5

Reset or remove the records

Restore state so the next run starts from the same baseline, and remove test data from shared environments afterwards.

!
Privacy Reminder

Never copy real customer records into a test data file. Use synthetic data that looks realistic but belongs to nobody, so a leaked file harms no one.

30Variables in Automated Pipelines

Real teams run load tests automatically, and variables are what make a single plan adaptable.

JMeter has a graphical interface for building and debugging plans, but actual load runs should use the command-line mode with no graphics. The graphical window consumes memory and processor time that the load generator needs. In command-line mode, you supply the plan, say where to write results, and pass any properties that steer the run.

flowchart LR
    A["Code change"] --> B["Build pipeline"]
    B --> C["Start JMeter without graphics"]
    P["Environment and size properties"] --> C
    S["Secrets from secret store"] --> C
    C --> R["Result file and summary report"]
    R --> G["Pass or fail gate"]
        
FIG 5 — A pipeline passes properties and secrets in, and gets results out

What to parameterise

Parameter

Target Environment

Host, port and protocol so the same plan tests development, staging or pre-production.

Parameter

Load Size

Number of users, ramp-up time and duration, so a quick smoke test and a full soak test share one plan.

Parameter

Data Location

The folder holding the data files, so each pipeline stage can supply its own.

Parameter

Credentials

Supplied from a secret store at run time and never saved in the plan.

Pass and fail gates

Pipelines need a clear verdict. You can combine assertions inside the plan, which mark individual requests as failed, with checks on the result summary, such as error percentage or response-time thresholds, to decide whether the build proceeds. Variables again help here: thresholds themselves can be properties, so a team can tighten a limit without editing the plan.

i
Make It Runnable Anywhere

Because every property has a default, anybody can open the plan on a laptop and run a tiny version locally, while the pipeline overrides the same properties for a serious run. One plan, many sizes.

31Troubleshooting Playbook for Variable Problems

When something goes wrong, match the symptom to its most likely cause, then confirm with a debug element.

SymptomMost likely causeFirst thing to check
Request shows a literal name with bracesVariable does not exist at that momentSpelling, case, scope and element order
Extracted value equals your “not found” defaultPattern or path did not matchCompare the actual response with your expression
Every user gets the same dataSharing mode or a shared property is wrongCSV sharing setting and where data is stored
Users run out of data earlyFile too small or recycling offRow count against users times iterations
First user fails to log inHeader row read as dataThe ignore-first-line setting
Value works once, then failsStale value carried into the next iterationWhether the extractor runs every loop
Request rejected as malformedEmpty variable broke the body or addressThe sent request in the results viewer
Property seems ignoredRead as a variable, or set on the wrong machineProperty function and the dash-G option
Generator slows during the runLarge stored values, debug tools or heavy scriptsListeners, extractor sizes and script caching
Duplicates across machinesEach worker has identical data or countersSplit files and offset counter starts

A calm method

Resist the urge to change several things at once. Reduce to one user, reproduce the failure, change one item, and rerun. Variable bugs are logical, not mysterious: every one has a spelling, scope, order or data cause. Work through those four in turn and you will find it.

“Spelling, scope, order, data: nearly every variable bug lives in one of these four places.”

32Glossary: Every Term in Plain English

Keep this chapter handy. Each term is explained the way you would explain it to a friend.

Term

Thread

One virtual user. A thread group is a crowd of identical virtual users running the same steps in parallel.

Term

Sampler

The element that actually sends a request, such as one web page fetch or one API call.

Term

Iteration

One complete pass of a thread through its steps. A thread may repeat many iterations.

Term

Ramp-up

The time JMeter takes to start all virtual users, so load builds gradually instead of arriving all at once.

Term

Correlation

Capturing a value from one reply and using it in a later request.

Term

Parameterisation

Replacing fixed values with variables so each user can send different data.

Term

Extractor

A post-processor that reads a response and stores part of it in a variable.

Term

Assertion

A check that marks a request as failed when the reply does not look right.

Term

Pre-processor

An element that runs just before a sampler, often to prepare a value.

Term

Post-processor

An element that runs just after a sampler, usually to extract data from the reply.

Term

Timer

An element that adds a pause before a request, imitating the thinking time of a real person.

Term

Listener

An element that records or displays results, from a simple table to a graph.

Term

Scope

The set of elements an item affects, decided by where it sits in the test plan tree.

Term

Default Value

The text an extractor stores when it finds no match.

Term

Load Generator

The machine running JMeter and producing the traffic.

Term

System Under Test

The website, API or service that receives the traffic and is being measured.

Simple Analogy

Learning these terms is like learning the names of kitchen tools. Once you know what a whisk, a ladle and a colander are called, every recipe suddenly becomes readable.

33A Practical Learning Path

Reading is a start; practice makes the ideas stick. Here is a sensible order for building skill.

1

Practise with one fixed setting

Create a plan that sends a single request, then move the server name into a user defined variable and confirm the request still works.

2

Add a small data file

Feed three or four different values from a file and watch each user pick a different row in the results viewer.

3

Capture one value from a reply

Choose a simple public test service, extract a value from its reply, and show it using a debug element.

4

Chain two requests

Use the captured value in a second request, proving that correlation works end to end.

5

Make it configurable

Replace fixed numbers of users and loops with properties that have defaults, and run the plan with different values from the command line.

6

Break it on purpose

Misspell a name, move an extractor out of scope, or delete a data row. Seeing each failure teaches you its fingerprint, which makes real problems quick to recognise.

7

Scale up carefully

Increase users gradually while watching the load generator, and apply the performance habits from earlier chapters.

i
Practice Rule

Practise against a system you own or have explicit permission to test. Sending heavy traffic to other people’s services can disrupt them and may break their terms of use.

Where this skill leads

Once variables feel natural, the same ideas carry over to distributed testing, pipeline integration, custom reporting and even other performance tools, because the underlying concepts of per-user data, shared configuration and correlation are universal.

34Advanced Nuances That Trip Up Experienced Testers

Once the basics feel comfortable, a handful of subtle behaviours decide whether a large plan stays reliable.

Nested references resolve from the inside out

You can place a variable reference inside another reference or inside a function’s arguments. JMeter resolves the innermost reference first, then works outward. This is how a plan can build a name such as “price for item three” from a counter and then read the variable with that assembled name. It is elegant but fragile, because a single typo in either layer produces a name that does not exist, and the failure is again silent.

Commas inside function arguments

Functions separate their arguments with commas. If a value you pass in itself contains a comma, such as a sentence or a formatted number, JMeter will think you started a new argument. The fix is to escape that comma with a backslash. Forgetting this is a classic cause of functions returning odd results while the plan looks perfectly reasonable.

Setup and teardown groups are separate threads

The special setup and teardown thread groups run in their own threads, before and after the main groups. Because variables belong to a thread, anything stored in a variable inside a setup group is not automatically visible to the main groups. Passing values between them requires a property, which is shared by all threads.

Reusable fragments and scope

When you reuse a block through an include or module controller, the block runs inside the calling thread and sees that thread’s variables. This is convenient, because a shared login step simply writes the token into the caller’s dictionary. It also means a fragment silently depends on names defined elsewhere, so document the names a fragment reads and writes.

Nuance

Disabled Elements Do Nothing

A disabled extractor or counter creates no variable. A request later in the plan then finds nothing and keeps the literal name.

Nuance

Threads Start at Different Times

With ramp-up, early threads reach later steps long before late threads start. Never assume all users are at the same step.

Nuance

Shared Properties Race

Two threads writing the same property at nearly the same moment can overwrite each other. Write shared properties once, from a setup step.

Nuance

Overwriting Is Silent

Creating a variable with a name that already exists replaces the old value without warning. Unique, descriptive names prevent accidents.

!
Name Collisions

Two different extractors writing the same reference name will fight over it. The later one wins, and the earlier value vanishes. Give every extractor its own distinctive name unless you deliberately want overwriting.

A short quick-reference of helpful functions

NeedKind of functionWhy it helps
Read a run-wide setting with a fallbackProperty reader with defaultPlan runs standalone and is still steerable
Publish a value for other thread groupsProperty writerBridges the private variable boundary
Unique id for each requestUUID generatorNo coordination required
Date or time in a chosen formatTime functionRealistic timestamps and date fields
Random text of a given lengthRandom string functionUnique emails and names
Numbers that count upCounter function or elementSequences and unique numbers
Resolve a name that is built dynamicallyVariable-name resolverWalk through rows of related variables
Split a text into numbered piecesSplit functionTurn a delimited list into variables for looping
“Every silent behaviour in JMeter is a place where a careful tester adds a check.”

35Frequently Asked Questions

Q1Why does my request still show the variable name with braces?

The name does not exist in that thread at that moment. Check spelling and capitalisation, confirm the element that creates it ran earlier, and make sure it is within scope. If it came from the command line, it is a property and must be read with the property function.

Q2Can two thread groups share a variable?

Not directly. Variables belong to one thread. To pass a value across thread groups, write it as a property in one place and read it in another, keeping in mind that all threads see the same property.

Q3What is the difference between a variable and a property in one sentence?

A variable is private to one virtual user and changes during the run, while a property is shared by the whole JMeter process and is normally set before the run starts.

Q4Why did my User Defined Variables value not change halfway through the test?

Those elements are processed once at the start of the test regardless of where they sit. Use a pre-processor, counter, function or script for values that must change during execution.

Q5How do I stop two users from using the same account?

Use a CSV Data Set Config with all-threads sharing, disable recycling, and enable stop thread at end of file. When distributing the test, give each machine a different slice of the data.

Q6Why is my extracted value empty or showing my default?

The pattern or path did not match the actual response. Open the response in a results viewer, compare it character by character with your expression, and watch for different formatting, encoding or response differences between runs.

Q7Are variables case sensitive?

Yes. Two names that differ only in capital letters are two separate variables, which is a common source of silent failures.

Q8Can a variable hold a number I can add to?

Variables store text. Use a calculation function or a short script to convert the text to a number, do the arithmetic, and write the result back as text.

Q9Do variables survive between test runs?

No. Each thread’s dictionary is discarded when the thread ends, and a new run starts clean. Only files and configuration you provide persist.

Q10Is it safe to put passwords in a variable?

It is safe inside the running test, but unsafe inside a saved plan file that others can read. Supply secrets at run time through your pipeline’s secret handling and use dedicated test accounts.

Q11Why do my distributed workers create duplicate data?

Each worker reads its own data file and keeps its own counters. Split the files, offset counter start values per worker, or use UUID generation so uniqueness does not depend on coordination.

Q12Should I use scripts or built-in elements?

Prefer built-in elements whenever they can do the job. They are faster, clearer to teammates and easier to debug. Reserve short, cached scripts for genuine gaps.

Q13How can I tell whether a value is a variable or a property when reading someone else’s plan?

Look at how it is read. A plain dollar-brace name is a variable lookup. A property is read through the property function, often with a default value beside the name. Also check the command that launches the test: anything passed with the dash-J or dash-G options is a property.

Q14Why does one extractor seem to overwrite the result of another?

They share the same reference name. The extractor that runs later replaces the earlier value. Give each extractor a unique, descriptive name unless you intentionally want the newest value to win.

Q15Is it fine to run big tests from the graphical window?

Use the graphical window to build and debug with a handful of users. For real load, run without graphics, because the interface consumes memory and processor time that the load generator needs for generating traffic.

Q16How many variables is too many?

There is no fixed limit, but readability and memory both suffer when plans accumulate unused or duplicated names. Keep only variables that are referenced, store small values, and prune the rest during regular reviews.

36Summary and Key Takeaways

JMeter variables are the memory of your virtual users. They let one test plan imitate thousands of distinct people, carry server-issued values from one request to the next, and keep configuration in a single tidy place. The central ideas are simple: variables are private to one thread, properties are shared by the whole process, values are text, order and scope decide when a name exists, and anything that fails silently should be made to fail loudly. Master those ideas and your load tests will describe the system under test honestly rather than describing your own mistakes.

Key Takeaways

  • Variables are per thread — each virtual user owns a private dictionary, so users never overwrite each other’s data.
  • Properties are global — use them for run-wide settings and for passing values across thread groups, and read them with the property function.
  • Choose by origin — fixed settings use User Defined Variables, per-user data uses CSV files, server values use extractors, invented values use counters, random elements or functions.
  • Order matters — a value only exists after the element that creates it has run, and User Defined Variables are all processed at test start.
  • Fail loudly — give extractors distinctive defaults and add assertions so missing data becomes a visible error, not a silent one.
  • Plan for scale — keep stored values small, remove debug tools before real runs, and split data and counters across distributed workers.
  • Protect secrets — keep credentials out of plan files and result files, and use dedicated low-privilege test accounts.