Reusing setup across tests with scenarios
A scenario pairs a set of data rows with a set of checks, so one group of asserts runs across every row without being written out once per row.
Last updated: 15 Oct, 2026 · Promptfoo 0.124.1
The Loading test datasets from a file glob lesson gathered many tests, but each still carried its own assert. Often several rows should face the same checks: every answer from the handbook should carry a fact, every miss should say so. A scenario states the rows once and the checks once, and promptfoo runs each check over each row.
The shape of a scenario
The scenarios key is a list. Each scenario has a config, a list of data rows that each set the vars for a run, and a tests list holding the asserts. Promptfoo pairs every row in config with every entry in tests, so one set of checks covers all the rows.
providers:
- file://support_bot.py
prompts:
- "{{question}}"
scenarios:
- description: questions the handbook answers
config:
- vars:
question: Where is order A17?
- vars:
question: How long does a refund take?
tests:
- assert:
- type: not-contains
value: could not find
- description: a question the handbook misses
config:
- vars:
question: Where is order B99?
tests:
- assert:
- type: contains
value: could not find
The first scenario lists two questions the handbook answers and one check, that the reply is not the fallback, so that one assert runs over both rows. The second scenario is a single miss with the opposite check. Three rows in all, each graded by its scenario's test.
View the code here
# the shop support bot: find the answer in the handbook, or say it is missing
HANDBOOK = {
"a17": "Order A17 shipped on 3 March by courier.",
"refund": "Refunds take five working days.",
}
def call_api(prompt, options, context):
asked = prompt.lower() # the rendered prompt is the question
for key, line in HANDBOOK.items():
if key in asked:
return {"output": line}
return {"output": "I could not find that in the handbook."}
Running the scenarios
One eval runs both scenarios. The two rows of the first and the one row of the second become three test cases, each checked by the asserts of its own scenario.
promptfoo eval --no-tableStarting evaluation eval-nXS-2026-10-14T19:17:06 Running 3 test cases (up to 4 at a time)... Creating cache folder at ~/.promptfoo/cache. ✓ Eval complete (ID: eval-nXS-2026-10-14T19:17:06) » View results: promptfoo view » Share with your team: https://promptfoo.app » Feedback: https://promptfoo.dev/feedback Results: ✓ 3 passed (100%) 0 failed (0%) 0 errors (0%) Duration: 0s (concurrency: 4)
What the scenarios ran
- The first scenario's check ran twice, once per row in its
config, without being written out for each. - The second scenario had its own check, the opposite one, so the miss was expected to say it could not find the order.
- Three cases in all. Each scenario multiplied its rows by its tests, and the two scenarios' results were reported together.
Scenarios vs plain tests
| Plain tests | A scenario | |
|---|---|---|
| Asserts | Written per test | Written once, run over every row |
| Data rows | One vars block each | A config list of rows |
| Repetition | Same check copied per row | Check stated once |
| Best for | Rows with different checks | Rows sharing one set of checks |
Where scenarios help
- One set of checks over many rows, such as a correctness and a tone assert applied to every answer.
- Grouping rows by expectation, a scenario for answers the bot should know and another for ones it should not.
- Keeping a suite short, stating a shared check once rather than copying it into every test.
config against every entry in tests, so the count is the two multiplied. A scenario with four rows and three tests runs twelve cases, not four, which is easy to forget when a scenario grows.Related
- Previous: Loading test datasets from a file glob
- Next: Deterministic checks: contains, equals, regex
- Reference: Promptfoo scenarios docs
- Add a second check to the first scenario's
testsand watch both run over each of its rows. - Add a third row to the first scenario's
configand count the cases the run reports. - Move the B99 row into the first scenario and read why its check now fails there.
Every expert started right here.