Match Limit (options.times) v3.7.0
options.times limits how many times a stub can be matched. Past the limit the stub is exhausted and no longer used — which is how you assert an exact call count, or make the second attempt of a retry hit a different stub.
- times: 0 (default) — unlimited matches; the stub never exhausts
- times: 1 — exactly one match, then the stub is exhausted
- times: N — up to N matches, then exhausted
- Negative values — invalid; rejected at validation
When a stub is exhausted, subsequent matching requests will not use it. If it was the only matching stub, the call will fail with StubNotFound.
Basic Usage
YAML Format
# Stub that matches exactly once
- service: UserService
method: GetUser
input:
equals:
id: "one-time-user"
output:
data:
id: "one-time-user"
name: "Temporary"
options:
times: 1
# Stub that matches up to 3 times
- service: PaymentService
method: ProcessPayment
input:
equals:
orderId: "order-123"
output:
data:
status: "success"
options:
times: 3
# Default: unlimited (omit options or set times: 0)
- service: HealthService
method: Check
input:
contains: {}
output:
data:
status: "ok"JSON Format
{
"service": "UserService",
"method": "GetUser",
"input": {"equals": {"id": "one-time-user"}},
"output": {"data": {"id": "one-time-user", "name": "Temporary"}},
"options": {"times": 1}
}Use Cases
1. One-Shot Stubs (Error After First Call)
Simulate a service that fails after the first successful call:
# Succeeds once, then subsequent calls get StubNotFound
- service: CacheService
method: Get
input:
equals:
key: "expiring-key"
output:
data:
value: "cached"
options:
times: 12. Retry Testing
Allow exactly N retries before failing:
# Client retries 3 times; stub matches 3 times, then fails
- service: ExternalService
method: Call
input:
equals:
attempt: 1
output:
data:
result: "success"
options:
times: 33. Different Limits per Input
Different inputs can have different match limits. Example: Ben matches once, Alice twice.
# Ben: exactly 1 call, then exhausted
- service: helloworld.Greeter
method: SayHello
input:
equals:
name: "Ben"
output:
data:
message: "Hello Ben"
options:
times: 1
# Alice: up to 2 calls, then exhausted
- service: helloworld.Greeter
method: SayHello
input:
equals:
name: "Alice"
output:
data:
message: "Hello Alice"
options:
times: 2Flow: SayHello("Ben") → ok; SayHello("Ben") again → StubNotFound. SayHello("Alice") × 2 → ok; third call → StubNotFound.
4. Fallback After Exhaustion
Use priority so a second stub handles calls after the first is exhausted:
# First 2 calls: specific response
- service: QuoteService
method: GetQuote
priority: 10
input:
equals:
symbol: "AAPL"
output:
data:
price: 150.0
options:
times: 2
# After exhaustion: fallback
- service: QuoteService
method: GetQuote
priority: 1
input:
equals:
symbol: "AAPL"
output:
error: "Service unavailable"
code: 14Concurrency
The counter is mutex-protected. Under concurrent load exactly times matches succeed; extra calls receive StubNotFound.
Notes
- Exhausted stubs remain in storage; they are only excluded from matching
/api/stubs/unusedand/api/stubs/usedreflect whether a stub has been matched (used count > 0)- Clearing the storage (
DELETE /stubsor restart) resets all match counts