Docs/Seeding a fresh test user
#

Seeding a fresh test user

Mint a unique account with its own inbox for every run, create it in your app from a seed flow, and hand the login to your tests.

Give every run its own brand-new account. A seed flow with a credential output mints a unique email (with an inbox), creates the account in your application, and hands the login to the tests that need it — so signup, invite, onboarding and account-settings flows never collide with each other or with leftover data.

  • Unique per run — each run gets prefix+<id>@… with its own inbox, so parallel runs never share a user.
  • Reusable — one seed serves every test that needs "a fresh user"; no per-test copies.
  • Self-cleaning — the platform removes the minted credential after the suite; a paired teardown removes the account.

01When to seed a user (and when not to)

Use a persona when a test only needs some logged-in account of a role — that is the default for authenticated tests and needs no seed.

Seed a user when the test needs a new account it owns:

  • signup, invite acceptance, onboarding and first-login flows
  • anything that changes the account itself — profile, password, MFA enrolment, leave org, delete account
  • a fresh inbox for verification codes, invite links or magic links
  • a user with no history, so counts and empty states are deterministic

On a sandbox, a sandbox action bound to a persona can mint the account instead (for example a provision-tenant action that maps to the Admin persona). Tests then just use the persona and need no seed node — but they become sandbox-only. The rest of this guide covers the seed-flow path, which works on any environment.

02How it works

When a run references the seed, before the seed's own steps run, Cofactor:

  1. Mints an email address from the output's email prefix plus the run id — e.g. qa-invite+3fa92c1b@testuser.runcofactor.com.
  2. Creates a short-lived credential on the application, with the username set to that address and the password you configured.
  3. Creates an inbox for the address when Create email inbox is on, so the seed and its tests can read mail sent to it — your app's sender domain must be on the allow list; see Setting up Cofactor agents to receive email.
  4. Exposes the fields to the seed's steps as $var.<output key>.username and $var.<output key>.password.
  5. Binds the credential to any later login step that points at the output, and deletes it after the suite finishes.

Minting the credential does not create the account in your application. That is the seed's job — see step 2 below.

03Declare the credential output

  1. Open Test Data → Seed Data and create a seed flow (or open an existing one).
  2. Add an output, give it a Data key (for example new_user) and turn on Provisions a credential.
  3. Fill in the credential fields:
FieldWhat to set
Username$generate.email — replaced with the minted address on every run.
PasswordA literal your app's password policy accepts. It is the same on every run; uniqueness comes from the email.
Email prefixThe local-part prefix (qa-invite). Name the seed so inbox traffic is attributable.
Create email inboxOn whenever a step needs to read a message sent to the user. Without it the address exists but nothing captures its mail. Mail is only delivered from allowed sender domains.
Provision SMS phone numberOn for OTP-by-text flows.
Login URL / Login instructionsWhere and how the login agent signs in as this user.
Account needs provisioningOnly if a manual step must happen before first login.

Add plain outputs alongside it for every id the tests or the teardown will need — an org id, a user id, an invite token. Declare only what the seed actually exports; a declared-but-unexported output is reported as missing on every run.

From the CLI, the same shape is a JSON file:

bash
cofactor workflow seed create --from-file seed.json
json
{
  "name": "Create invited user",
  "outputs": [
    {
      "dataKey": "new_user",
      "name": "Invited user",
      "credentialOutput": {
        "propertyId": "<application id>",
        "emailPrefix": "qa-invite",
        "fieldMapping": { "username": "$generate.email", "password": "QaTest-Passw0rd!" },
        "createInboxRecipient": true,
        "loginUrl": "https://app.example.com/login"
      }
    },
    { "dataKey": "invite_token", "name": "Invite token" },
    { "dataKey": "organization_id", "name": "Organization ID" }
  ]
}

04Decide who creates the account

Testing signup itself? Leave the seed body empty. The minted email, password and inbox are all the test needs — it registers the account through your app's real signup form, reads the verification email, and lands signed in. A seed whose only node is End is valid and runs in under two seconds; its job is purely to mint the user. Keep the teardown for the account the test creates.

Testing anything that starts from an existing account? Make the seed create it. Prefer an API sequence; use browser steps only when no API exists. Typical shapes:

  • Invite — an admin creates or picks an org, then posts an invite for $var.new_user.username and exports the invite token. The test completes signup through the invite link.
  • Direct — post to your signup or users endpoint with $var.new_user.username and $var.new_user.password, export the user id. The account is ready to log in.
  • Verify — if signup emails a code, the seed can read it (the inbox already exists). Usually leave that step to the test when verifying is what the test proves.

For API sequence steps:

  • Authenticate as a persona, not a fixed credential, so the seed runs on every environment.
  • Use $var.ENV.api_url/... for the URL rather than a hard-coded host.
  • Export every id the teardown needs.

05Use the user in a test

  1. Add a Seed node that references the seed flow. Its outputs become $var.<key>.
  2. In the Acting as (login) node, set Credential source to From seed script and pick the credential output. Leave Logged in off when the test performs signup or first login itself; turn it on when the account is ready and the test starts signed in.
  3. Use $var.new_user.username and $var.new_user.password in form steps, and the plain outputs ($var.invite_token) where needed.
  4. For email steps, describe what to read — "open the verification email sent to $var.new_user.username and follow the accept-invite link". The agent reads the user's inbox itself; never hard-code a code in the step's inputs. Setup and troubleshooting: Setting up Cofactor agents to receive email.

In a spec file, put the seed first and reference the output as the actor:

yaml
---
name: Accept an admin invite
actor: seed-output new_user
authenticated: true
---
## Spirit
> An invited user can accept the invite and sign in.

## Journey
**Seed** Create invited user
**Navigate to** /invite/$var.invite_token
**Action — Sign up**
- field email: $var.new_user.username
- field password: $var.new_user.password

Several tests can reference the same seed; each run mints its own user, so they run in parallel without colliding. Tests that must share one user across flows run as ordered suite layers instead.

06Pair a teardown

The seed's teardown receives the same $var values and should delete the account through the API — the org if the seed created one, otherwise the user — using the exported ids. Cofactor removes the minted credential and inbox itself; only your application's records need the teardown. After publishing, check once that a run's account is actually gone; a seed that runs daily without a working teardown leaves one orphan per run.

07Checklist

  • Provisions a credential on with $generate.email as the username
  • Create email inbox on if any step reads mail
  • Seed steps create the account (or the body is empty because the test signs up) and export every id the teardown needs
  • API steps use a persona and $var.ENV.api_url
  • Login node source is From seed script, with a Seed node before it
  • No hard-coded verification codes in step inputs
  • Teardown deletes the account or org, verified once by hand
  • Seed and teardown published before the tests that use them