Functions, Custom Properties, Reusable Fragments, and Modular Plans: Core Concepts and Mental Model
Chapter 19 reduced hidden scripting by pushing standard behavior back into the JMeter tree. The next maintainability problem appears when a test plan grows: the same host, thread counts, setup/login sequence, assertions, and helper requests are copied into multiple scenarios. Copy/paste initially feels simple, but the copies drift. One scenario receives a timeout fix, another keeps an old path, and a CI run silently uses different defaults. Chapter 20 treats a JMeter project as code: configuration is externalized, reusable flows have named boundaries, paths are resolved deliberately, and the final runtime tree still makes workload semantics visible.
Learning objectives
- Explain how functions, properties, Test Fragments, Module Controller, and Include Controller fit into JMeter execution.
- Distinguish process-wide properties, thread-local variables, function evaluation, external files, and target/session state.
- Understand same-plan Module references versus external Include imports.
- Recognize path resolution and module naming as runtime correctness concerns.
- Inspect project/module/property state non-destructively before refactoring.
- Keep modularity visible enough that configured and achieved workload remain reviewable.
1. The practical problem: duplication becomes configuration drift
Imagine three scenarios that each copy a four-element “session bootstrap” sequence. When the endpoint changes, twelve tree nodes must be reviewed. If one copy keeps the old path or assertion, the test suite no longer represents one workload definition. A modular plan should reduce the number of definitions while preserving the number and order of runtime operations.
http://127.0.0.1:8020, synthetic
user/session identifiers, maximum 2 threads ×3 work loops, 25 ms
pacing, no credentials, no plugins, no remote engines, and no
external target. Never substitute a public/shared/production
endpoint for the modularity lab.
2. Mental model: project sources assemble into an ordinary thread-local runtime tree
Modularity changes where configuration is defined, not the fundamental JMeter execution model. Properties/functions resolve configuration, controllers assemble reusable elements, and the resulting samplers still execute inside ordinary JMeter threads against the same authorized target.
flowchart TD P[Property files + -J run settings] --> R[Functions such as __P / __property] F[Test Fragment definitions] --> M[Module Controller references] X[External fragment JMX] --> I[Include Controller + include prefix] R --> A[Assembled JMeter runtime tree] M --> A I --> A A --> T[Thread-local execution: vars / sessions / counters] T --> H[HTTP samplers to 127.0.0.1:8020] H --> J[JTL + jmeter.log] H --> E[Fixture session/work event evidence] G[Generator cwd / files / CPU / path state] --> V[Validity review] J --> V E --> V
The property layer supplies environment/run configuration. Functions
such as __P read those properties when JMeter evaluates
fields. A Module Controller points to a controller/fragment already
loaded into the same JMX. An Include Controller loads an external
JMX Test Fragment. These sources are assembled into the effective
runtime tree. Once execution begins, each JMeter thread still owns
its ordinary variables, HTTP/session state, counters, and samples.
Modular structure therefore changes code organization and
configuration lookup—not the fundamental thread/workload model.
JTL/fixture events prove what actually executed, while generator
path/file state proves that the intended modules were loaded.
3. Function versus variable versus property
| Mechanism | Ownership / evaluation | Use in this chapter |
|---|---|---|
| JMeter function | Evaluated where the containing field is processed. |
__P reads run/load settings;
__threadNum creates a per-thread synthetic user
name.
|
Variable ${{NAME}} |
Thread-local JMeterVariables after the variable-producing element runs. |
SESSION_ID, sequence counters, extracted
response state.
|
| Property | Shared within one JMeter JVM/process. | Target host/port, threads, loops, pacing, RUN_ID/VARIANT; immutable during the run. |
| External data/file | Filesystem state visible to the injector. | Property file, external fragment JMX, optional committed synthetic fixtures. |
Properties are configuration. Variables are virtual-user state.
Functions are evaluation mechanisms. Mixing those roles—such as
putting SESSION_ID in a property—creates cross-thread
contamination.
4. __P is convenient—and its default matters
${__P(target.host,127.0.0.1)} returns the
target.host JMeter property or the explicit fallback.
Current JMeter defines an omitted __P default as
1. That makes
${__P(threads)} syntactically valid even when the
property was forgotten—which can silently turn a planned 20-thread
run into one thread.
For operational plans, use explicit safe defaults and preflight the resolved property set instead of relying on hidden fallback behavior.
5. __property is useful for inspection
${__property(user.dir,JMETER_USER_DIR)} reads the JVM
user.dir property and can also store it into a JMeter
variable. This is useful in a one-thread debug/preflight element to
prove the current working directory/path context. Do not add a debug
sampler to the measured workload merely to collect a value that the
launcher can record once.
6. __threadNum belongs in thread execution, not
Configuration Elements
${__threadNum} returns a number local to the containing
Thread Group. Use it directly in the bootstrap sampler field to
create user-1, user-2, and so on. Current
JMeter explicitly warns that __threadNum does not work
correctly inside Configuration Elements such as User Defined
Variables because those are processed by a separate thread.
7. Module Controller: reuse inside one loaded plan
A Module Controller substitutes a selected controller/fragment already loaded in the JMeter GUI/test plan. The target fragment can live under a Test Fragment or disabled/dummy Thread Group. The module reference is convenient when several scenarios inside one JMX need the same sequence.
Fragment/controller names must be unique enough for JMeter to find
the correct path after reload. Avoid multiple generic names such as
“Simple Controller” or “Login.” Use names such as
FRAG.session.bootstrap.v1.
8. Include Controller: reuse across JMX files
Include Controller loads an external JMX that contains a Test Fragment. A debug Thread Group may also be present in that external file, but JMeter ignores it during include. This makes Include appropriate when the same fragment must be versioned independently and reused by several top-level plans.
Important current constraints:
- the Include filename field does not support variables/functions;
-
includecontroller.prefixcan supply a common prefix/path; - if prefix+filename is not found, JMeter attempts the filename relative to the JMX launch directory;
- Cookie Manager/User Defined Variables should stay in the top-level plan rather than the included file.
9. Test Fragment is a definition boundary, not a workload by itself
Test Fragment is designed for Module/Include use. Since JMeter 2.13, a Test Fragment used with Module Controller is disabled by default so it is not executed independently and again through the Module reference. The definition should contain only the reusable sequence and assumptions it owns; environment-wide configuration stays outside it.
10. State checklist before modularizing
| State | Question |
|---|---|
| Generator | JMeter/Java version; cwd/user.dir; project root; accessible fragment/property files; CPU/heap? |
| Thread/arrival | Threads/loops/pacing unchanged by the refactor? Which controller runs once versus per loop? |
| Component scope | Where do defaults, fragments, Module/Include Controllers, assertions, timers and cleanup sit? |
| Variables/properties | Which values are per-user vars and which are immutable JVM properties? |
| Protocol/session | Does the fragment create one session per thread, and is cleanup after the work loop? |
| Target | Same localhost endpoint/work distribution before/after modularization? |
| Files/paths | Exact main JMX, fragment JMX, property file, launcher, cwd and include prefix? |
| Artifacts | JTL, jmeter.log, command manifest, target events, dependency/duplication note? |
| Trust/privacy | No secrets in property files, manifests, JMX or logs? |
| Validity | Did organization change only definitions, not achieved work/sample counts? |
11. Non-destructive project inspection first
Before changing a JMX, inventory reusable candidates and path dependencies:
PowerShell:
Get-ChildItem -Recurse -File . |
Select-Object FullName, Length
Get-ChildItem -Recurse -Filter *.jmx |
Select-String -Pattern "TestFragmentController|ModuleController|IncludeController|__P|__Random|__UUID"
Get-Content .\config\local.properties
Also inspect one copy of each duplicated setup flow in the GUI: parent scope, HTTP Defaults/Cookie Manager, extractors/assertions, and every variable it produces. A fragment is safe only when its required inputs and outputs are understood.
12. Draw the module dependency graph before extraction
For the mandatory lab the graph is intentionally acyclic:
main-module.jmx
└── Module Controller -> FRAG.session.bootstrap.v1 (same JMX)
main-include.jmx
└── Include Controller -> session-bootstrap.jmx
└── Test Fragment -> FRAG.session.bootstrap.v1
No fragment includes the main plan and no fragment includes itself. A dependency graph that cannot be described simply is already a maintainability warning.
13. Runtime order must remain visible
The target workload for every variant is:
Per thread:
1. Create one synthetic session (once)
2. Repeat WORK exactly ${__P(loops,3)} times with pacing
3. Delete exactly that thread's SESSION_ID
Modularity is green only if the same sequence and counts are observed in JTL and fixture events.
14. DevOps connection
A modular JMeter project can be reviewed like application code: property contracts are diffable, fragment dependencies are named, launcher behavior is reproducible, paths are testable in CI, and duplication is measurable. This makes performance plans maintainable assets rather than GUI documents that only work on the author's workstation.
Knowledge check
What is the core difference between Module and Include Controller?
Module references a controller/fragment already loaded in the same plan; Include loads an external JMX/Test Fragment.
Why is __P(threads) without a default risky?
If the property is missing, current JMeter returns 1, which can silently change configured load.
Why should SESSION_ID remain a variable?
It is per-virtual-user session state; a process-global property would allow threads to overwrite/share it.
Why can Include Controller become cwd/path brittle?
Its filename cannot use functions/variables and depends on includecontroller.prefix/path fallback to the JMX launch directory.
What proves modularization did not alter workload semantics?
The same configured thread/loop model produces the same valid target session/work/cleanup counts and result evidence.
Official references and version notes
- JMeter Component Reference — Module Controller — runtime substitution of an already loaded controller/fragment and unique fragment naming.
-
JMeter Component Reference — Include Controller
— external JMX/Test Fragment use, filename limitations,
includecontroller.prefix, and path fallback behavior. - JMeter Component Reference — Test Fragment — reusable fragment semantics with Module/Include Controllers.
- JMeter Functions — __P — simplified command-line property lookup and default behavior.
- JMeter Functions — __property — general JMeter property lookup and optional variable assignment.
- JMeter Functions — __threadNum — thread-group-local thread numbering and Configuration Element restrictions.
- JMeter Functions — __Random — pseudo-random values and optional variable assignment.
- JMeter Component Reference — Random Variable — seeded/per-thread reproducible random-number option.
-
JMeter Getting Started
—
-q/-J, property files, and JMeter configuration loading. - JMeter Best Practices — GUI authoring/debugging and non-GUI execution for load.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive statements were 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;
JMeter 5.6.3 requires Java 8+. A Module Controller substitutes an
already loaded controller/fragment into the active runtime path;
fragments referenced by Module Controller need unique names
because JMeter uses the controller/parent name path to find them
after reload. Include Controller is for external JMX/Test Fragment
content. Its Filename field does not support
variables/functions. The
includecontroller.prefix property can prefix the
filename; if prefix+filename cannot be found, JMeter attempts the
filename relative to the JMX launch directory. Top-level Cookie
Manager/User Defined Variables belong in the main test plan rather
than an included JMX because included copies are not guaranteed to
work as expected. __P(name,default) is intended for
command-line properties; without an explicit default it returns
1. __threadNum is local to its Thread
Group and should not be placed in Configuration Elements such as
User Defined Variables because those are processed by a separate
thread. For reproducible random numeric data, the Random Variable
Config Element exposes a seed/per-thread option; the simple
__Random function does not expose an equivalent seed
parameter.
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.