Write a test
Use this guide to write a test that is reliable and cheap to maintain. For every field, see the test file format.
Write one action or check per step
steps:
- when: I click "Add to cart"
- then: the cart shows 1 item
- Use the words the app shows:
"Add to cart", not a CSS selector or XPath. - Do not write waits or sleeps.
- Make every
thencheckable on the page: a text, a count, a control's state, or the page shown. Avoid exact dynamic values such as dates, ids and prices. - Use
andorbutto continue the step before.
Start logged in with a role
role: user
start: /transaction/new
The role's login test logs in once per environment, and every test with that role starts logged in. Do not write login steps. For an app without login, or a registration page, leave role: out.
Never put a password or token in a test. Use variables such as $USERNAME and $PASSWORD.
Set up a starting state with a background
background:
- given: I have at least one contact
Background steps are visible and recorded like normal steps. Prefer them to hidden hooks.
Reuse steps with shared steps
List what the project already has, then print the one you want to reuse:
teststream shared-steps list
teststream shared-steps get new-transaction
Use the entry named new-transaction for the example below, or replace it with a shared flow defined in your project.
Then use it:
steps:
- use: new-transaction
with: { amount: "5" }
Shared steps are made on the Shared steps screen in the app. A change to them reaches every test that uses them, so people make them, not scripts. If steps repeat and none fit, write them in the test and suggest a shared step to a teammate.
Run the same test for several values
steps:
- use: new-transaction
with: { amount: "<amount>" }
- then: the transaction appears with amount "<shown>"
examples:
- { amount: 5, shown: "$5.00" }
- { amount: 12.5, shown: "$12.50" }
The test runs once per row. <column> is replaced by the row's value.
Generate test data
Declare named values at the top of the test and use them as $NAME:
data:
EMAIL: { faker: internet.email }
TITLE: { faker: lorem.sentence }
steps:
- when: I enter "$EMAIL" in the email field
- and: I enter "$TITLE" as the title
Each value is generated once per run and stays the same across every action, check, replay and retry. New runs get new data. To reproduce a run's data, set dataSeed: "<seed>". See the generators.
Skip a step that may not apply
- when: I dismiss the cookie banner
optional: true
Organise tests
Set folder: to group tests. Filter by folder, tag or text when you list and run:
teststream tests list --folder Transactions
teststream tests list --q "pay contact"
Check and propose
teststream tests validate pay-contact.yaml
teststream tests push pay-contact.yaml --branch feature/pay
validate reports problems with line numbers. push proposes the test on the branch, where it waits for review.
Do not weaken a check to make a test pass
A failing check is information. If the app is wrong, the test stays red. Change a test only when the app changed on purpose or a step was worded badly. Investigate failures shows how to tell which.
Steps with consequences
A step that pays, purchases, deletes, sends or publishes runs for real. Get a person's agreement first, and keep such steps to a minimum.