05 — Feeders
A feeder is a data source that injects values into each virtual user's session. Use feeders to parameterise requests with unique usernames, product IDs, search terms, etc.
Feeding strategies
| Strategy | Method | Behaviour |
|---|---|---|
| Queue | .queue() |
Each record consumed once; fails if exhausted |
| Random | .random() |
Pick random record each time |
| Shuffle | .shuffle() |
Shuffle then consume in order |
| Circular | .circular() |
Round-robin, wraps around |
Default (no method call) is queue.
CSV feeder
File: src/test/resources/data/users.csv
// Java
FeederBuilder<String> feeder = csv("data/users.csv").circular();
ScenarioBuilder scn = scenario("Login")
.feed(feeder)
.exec(http("Login")
.post("/login")
.formParam("username", "#{username}")
.formParam("password", "#{password}")
.check(status().is(200)));
// Kotlin
val feeder = csv("data/users.csv").circular()
val scn = scenario("Login")
.feed(feeder)
.exec(
http("Login").post("/login")
.formParam("username", "#{username}")
.formParam("password", "#{password}")
.check(status().`is`(200))
)
Tab-separated values
Separator override
JSON feeder
File: src/test/resources/data/products.json
[
{"id": 1, "name": "Widget", "price": 9.99},
{"id": 2, "name": "Gadget", "price": 24.99},
{"id": 3, "name": "Doohickey", "price": 4.49}
]
// Java
FeederBuilder<Object> feeder = jsonFile("data/products.json").random();
scenario("Browse product")
.feed(feeder)
.exec(http("Product").get("/products/#{id}"))
Programmatic feeder
Create an Iterator<Map<String, Object>> from any source.
Java — infinite random data
import java.util.Map;
import java.util.Random;
import java.util.Iterator;
Random rng = new Random();
Iterator<Map<String, Object>> feeder = Stream.generate(() ->
Map.<String, Object>of(
"userId", rng.nextInt(100_000),
"amount", rng.nextDouble() * 1000
)
).iterator();
scenario("Random payments")
.feed(feeder)
.exec(http("Pay").post("/payments")
.body(StringBody("""{"userId":#{userId},"amount":#{amount}}"""))
.asJson())
Kotlin — infinite random data
import java.util.Random
val rng = Random()
val feeder = generateSequence {
mapOf(
"userId" to rng.nextInt(100_000),
"amount" to rng.nextDouble() * 1000
)
}.iterator()
val scn = scenario("Random payments")
.feed(feeder)
.exec(
http("Pay").post("/payments")
.body(StringBody("""{"userId":#{userId},"amount":#{amount}}"""))
.asJson()
)
Record per iteration vs record per user
By default feed() advances the feeder once per user per call. To advance multiple records at once:
Feeding at a specific point
You can call .feed() anywhere in the chain — it doesn't have to be first:
scenario("Scenario")
.exec(http("Home").get("/"))
.feed(productFeeder) // load product data here
.exec(http("Product").get("/products/#{id}"))
Unzipping feeders (joining two feeders)
FeederBuilder<String> users = csv("users.csv").circular();
FeederBuilder<String> products = csv("products.csv").random();
scenario("Browse as user")
.feed(users)
.feed(products)
.exec(http("View product as user")
.get("/users/#{username}/products/#{id}"))
Feeder sharing
Feeders are shared across all virtual users by default — all users consume records from the same feeder instance. The feeding strategy controls how records are selected:
.queue()(default) — each record is consumed exactly once, in order. Once exhausted, an error is thrown..circular()— wraps around when all records have been consumed..random()— picks a random record each time..shuffle()— shuffles the records, then consumes them in order like queue.
Use
.queue()when each data row should be consumed exactly once (e.g. unique invite codes).