CSV files are great when you have pre-defined test data. But sometimes you need data generated on the fly -- a unique order ID for each request, today's date in a specific format, a random number for simulating varying quantities. JMeter has a rich set of built-in functions and a User Defined Variables config element for this.
Think of User Defined Variables as your test's configuration file. You define constants at the top of your test plan -- base URL, timeouts, environment-specific values -- and reference them everywhere. This way, switching from staging to production is just changing one value instead of 50 requests.
# Add: Test Plan > Add > Config Element > User Defined Variables
# Environment config:
BASE_URL = https://staging-api.example.com
APP_URL = https://staging.example.com
THINK_TIME = 2000
API_VERSION = v2
# Test parameters:
MAX_WAIT_MS = 5000
RETRY_COUNT = 3
TIMEOUT_MS = 30000
# Usage in requests:
# Server Name: ${BASE_URL}
# Path: /api/${API_VERSION}/users
# Constant Timer: ${THINK_TIME}User Defined Variables are processed ONCE at test plan initialization. They do NOT change during the test. If you need per-iteration values, use JMeter functions or JSR223 Pre/PostProcessors. A common mistake is putting a function like ${__time()} in a UDV and expecting it to return a different value each iteration -- it will return the same value (the time at test start) every time.
JMeter functions use the syntax ${__functionName(arguments)}. Note the double underscore. These are evaluated every time they are encountered, unlike UDVs. Here are the functions you will use most in real testing.
| Function | Purpose | Example | Output |
|---|---|---|---|
| ${__time()} | Current time in milliseconds | ${__time()} | 1710523200000 |
| ${__time(format)} | Current time in specified format | ${__time(yyyy-MM-dd HH:mm:ss)} | 2024-03-15 14:30:00 |
| ${__time(format,var)} | Current time saved to variable | ${__time(yyyy-MM-dd,today)} | Saves to ${today} |
| ${__UUID()} | Random UUID (v4) | ${__UUID()} | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
| ${__Random(min,max)} | Random integer in range | ${__Random(1,100)} | 47 |
| ${__Random(min,max,var)} | Random integer saved to variable | ${__Random(1000,9999,pin)} | Saves to ${pin} |
| ${__RandomString(len,chars)} | Random string of given length | ${__RandomString(8,abcdef0123456789)} | a3b7e2d1 |
| ${__counter(perThread)} | Incrementing counter | ${__counter(TRUE)} | 1, 2, 3... per thread |
| ${__threadNum} | Current thread number | ${__threadNum} | 5 (for thread 5) |
| ${__iterationNum} | Current iteration number | ${__iterationNum} | 3 (on third loop) |
| ${__P(prop,default)} | Read JMeter property | ${__P(users,10)} | Command-line: -Jusers=50 |
| ${__V(varName)} | Evaluate variable name dynamically | ${__V(user_${__counter(TRUE)})} | Value of user_1, user_2, etc. |
# POST /api/orders
# Body:
{
"orderId": "ORD-${__UUID()}",
"timestamp": "${__time(yyyy-MM-dd'T'HH:mm:ss.SSSZ)}",
"quantity": ${__Random(1,10)},
"customerId": "CUST-${__threadNum}-${__counter(TRUE)}",
"referenceCode": "${__RandomString(12,ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789)}"
}
# This generates unique data for EVERY request:
# Thread 1, Iteration 1:
# {
# "orderId": "ORD-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
# "timestamp": "2024-03-15T14:30:00.123+0530",
# "quantity": 7,
# "customerId": "CUST-1-1",
# "referenceCode": "X7K2M9P4B1QR"
# }
# Thread 1, Iteration 2:
# {
# "orderId": "ORD-f9e8d7c6-b5a4-3210-fedc-ba0987654321",
# "timestamp": "2024-03-15T14:30:02.456+0530",
# "quantity": 3,
# "customerId": "CUST-1-2",
# "referenceCode": "L5N8R3W0H6YJ"
# }This is a game-changer for CI/CD integration. Instead of editing the JMX file to change the number of users or the target environment, you use ${__P()} to read values from the command line. Your JMX becomes a template that works for any configuration.
# In your Thread Group:
# Number of Threads: ${__P(users,10)}
# Ramp-Up Period: ${__P(rampup,30)}
# Duration: ${__P(duration,300)}
# In your HTTP Request:
# Server Name: ${__P(host,staging-api.example.com)}
# Port: ${__P(port,443)}
# Run from command line:
jmeter -n -t load_test.jmx -Jusers=200 -Jrampup=60 -Jduration=1800 -Jhost=prod-api.example.com
# Or in CI/CD pipeline (Jenkins):
jmeter -n -t load_test.jmx \
-Jusers=${LOAD_TEST_USERS} \
-Jrampup=${LOAD_TEST_RAMPUP} \
-Jduration=${LOAD_TEST_DURATION} \
-Jhost=${TARGET_HOST} \
-l results.jtl \
-e -o report/When built-in functions are not enough -- you need a realistic phone number, a valid email with a timestamp, or business-logic-based data -- use a JSR223 PreProcessor with Groovy. It runs before the request and can set any variable.
// Generate realistic test data before each request
import java.text.SimpleDateFormat
// Unique email with timestamp to avoid duplicates
def timestamp = System.currentTimeMillis()
def email = "testuser_${timestamp}_${ctx.getThreadNum()}@test.com"
vars.put("gen_email", email)
// Random Indian phone number
def random = new Random()
def phone = "9" + String.format("%09d", random.nextInt(1000000000))
vars.put("gen_phone", phone)
// Random date of birth (age 20-50)
def cal = Calendar.getInstance()
cal.add(Calendar.YEAR, -(20 + random.nextInt(31)))
cal.add(Calendar.DAY_OF_YEAR, -random.nextInt(365))
def dob = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
vars.put("gen_dob", dob)
// Random address
def cities = ["Mumbai", "Delhi", "Bangalore", "Hyderabad", "Chennai", "Pune"]
def city = cities[random.nextInt(cities.size())]
vars.put("gen_city", city)
// Future date for booking (1-30 days from now)
cal = Calendar.getInstance()
cal.add(Calendar.DAY_OF_YEAR, 1 + random.nextInt(30))
def futureDate = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
vars.put("gen_booking_date", futureDate)
log.info("Generated data: email=${email}, phone=${phone}, city=${city}")Always use Groovy (not BeanShell) in JSR223 elements. Groovy is compiled and cached, while BeanShell is interpreted every time. In a test with 100 threads and 1000 iterations, the performance difference is massive. JMeter officially recommends Groovy for all scripting.
Q: How can you make a JMeter test plan configurable for different environments without editing the JMX file?
A: Use the ${__P(property, default)} function for values that should change between environments. For example, set the server name as ${__P(host,staging.example.com)} and thread count as ${__P(users,10)}. Then pass values via command line: jmeter -n -t test.jmx -Jhost=prod.example.com -Jusers=500. This approach makes the JMX file a reusable template and is essential for CI/CD integration where the same test plan runs against different environments with different load levels. You can also use User Defined Variables for values that stay constant within a test run but might change between test plans.
Key Point: User Defined Variables are constants set once at test start. JMeter functions like __UUID(), __time(), __Random() generate dynamic data per-request. Use __P() for CI/CD integration to pass values from command line.