← All posts
Guide

Repeatable test data with the unique token

A sign-up test can fail on its second run because the email address it created already exists. Use {{unique}} in created values to reduce repeat-run collisions. The token is run-scoped, so scenarios sharing a run still need distinct prefixes and appropriate cleanup.

Why fixed data breaks

A test that only reads existing data is less likely to cause write collisions, although sessions, data and environment can change. Given the account has an invoice named "INV-1001" verifies something already there; it doesn't matter how many times you check it.

Creation is different. The moment a step registers an email, names a new project, or adds a record, that value is now taken. Hardcode it and you get two failure modes:

  • Repeat runs collide. Run the test today and it registers casey@example.com. Run it tomorrow and the app rejects the address as already in use. The test fails on data left behind by its own previous run, not on a real regression.
  • Parallel runs collide with each other. Two runs firing at once — a manual run while a scheduled automation is mid-flight, or two scenarios in the same suite — both try to create Project Alpha. One wins, one gets a duplicate-name error, and the failure looks random.

Both failures are noise. They tell you nothing about your app and they erode trust in the suite, because a green result now depends on remembering to clean up by hand between runs.

What the token does

Embed {{unique}} in created values to reduce collisions across repeated runs:

When the user signs up with email "casey+{{unique}}@example.com"
And the user creates a project named "Project {{unique}}"

{{unique}} expands to a fresh stamp at run time. Each run gets its own stamp, so the email your test registers today is different from yesterday's, and the implementation uses a millisecond timestamp, so it is not a strict global uniqueness guarantee. The literal in your Gherkin stays readable — you can see it's an email, you can see it's a project name — but the actual value is generated per run.

Two properties make it dependable:

  1. It's run-scoped, and stable within the run. Every occurrence of {{unique}} in a single run resolves to the same value. That's what lets a later step reuse what an earlier step created — sign up, then sign back in with the same address, or create a record and then assert its name appears in a list.
  2. It takes no setup. No arguments, no variable to define anywhere. No ordinary variable is required. A project variable named TESTVIBE_RUN_UNIQUE can pin the value; avoid that override when you expect fresh stamps.

Patterns that use it

Plus-addressing for sign-up. The +{{unique}} trick works when your mail provider supports plus addressing and your app treats those addresses as distinct; example.com is an illustrative domain:

Scenario: New user signs up and lands on the dashboard
  Given I am on the "Sign up" page
  When the user signs up with email "casey+{{unique}}@example.com"
  And the user sets a password and clicks "Create account"
  Then the dashboard should open

Create, then act on what you created. Because the token resolves to one value across the run, an earlier step and a later step agree on it:

Scenario: A created project appears in the list
  Given the user is signed in
  When the user creates a project named "Project {{unique}}"
  Then "Project {{unique}}" should appear in the project list

The Then isn't guessing at a name — it references the exact value the When produced.

Unique names for records, teams, and uploads. Anything the app stores under a name benefits: a team, an invoice draft, an uploaded file. Wrap the name and repeated runs stack up cleanly instead of tripping over each other.

When not to reach for it

The token is for values your test brings into existence. It's the wrong tool in two cases:

  • Referencing existing data. If a step points at a record that's already there — Given the account has an invoice named "INV-1001" — keep it a plain literal. Making it unique would point the test at something that doesn't exist. Stable, known records are exactly what you want for read paths.
  • Credentials and secrets. A sign-in password or API key isn't a value you invent per run; it has to match what's configured. Those use {{var:NAME}} and {{secret:NAME}} tokens instead, resolved from your project variables and secrets rather than randomly generated.

A quick rule: if the step would fail when run twice in a row because the value is "already taken," it creates data — wrap it. If it would fail because the value isn't there, it references data — leave it literal.

The payoff

Fresh values reduce one source of repeat-run failures. The token alone does not isolate scenarios, configurations, or VUs sharing a stamp, and a millisecond timestamp is not a strict global uniqueness guarantee. Give created entities distinct prefixes, retain a cleanup strategy, and rerun the suite in parallel to check the setup.

Want tests like these generated from plain descriptions and verified by actually running them? Get early access, or read Write good test instructions for more on repeatable scenarios.

Coming soon

Turn a described flow into a reviewable test.

Describe a flow in plain English. Generate a Playwright test, run it on your schedule, and review proposed fixes when your UI changes. Join the waitlist for early access.

  • Plain English in, real Playwright out
  • Runs in your CI, on your schedule
  • No credit card, unsubscribe anytime