← All posts
Best practices

Your first tests shouldn't write anything

Ask a team which flow matters most and you get the same answer everywhere: checkout, sign-up, the thing that creates the record the business gets paid for. So that's what they automate first, on day one, before anything else exists.

It's the right destination and the wrong starting point. The first tests you write aren't really testing your app. They're testing whether your testing setup works at all, and the flows that write data are the worst possible place to find that out.

The axis is statefulness, not size

"Simple" gets read as "short," which sends people looking for a small flow to start with. That's the wrong measure. A twelve-step read-only journey through six pages is simpler to own than a three-step delete.

The property that decides how hard a test is to live with is whether it leaves anything behind:

Read-only testWrite test
Run it twiceSame answerSecond run starts from different conditions
Run two at onceFineThey can collide on the same record
Run it on productionUsually fineUsually not
Needs cleanupNoYes, or a resettable environment
Reasons it can failThe app is brokenSix, and only one of them is a bug

That last row is the whole argument.

A read-only test has one failure mode

When a check that only reads goes red, something in the app changed. The page didn't render, the row isn't there, the label moved. You go look at the app.

When a test that creates a record goes red, you're in a decision tree. Did the app break? Or did a run get killed halfway through last night and leave a record with the name this run wants? Did two scenarios in the same run pick the same value? Did the cleanup step never execute because the test failed on the step before it? Did someone reset the seed data? Is the account you're testing with now at its plan limit because eight hundred test records accumulated in it?

You will get through that tree. The problem is what it teaches everyone watching. The first few red runs a team sees set their permanent prior about what red means. If the first three are test-data problems, you've taught the room that red means "run it again," and that lesson is far harder to un-teach than it was to teach. Trust is the actual scarce resource in a new suite, and it's cheapest to buy at the start.

Your first failure is going to be plumbing anyway

Nobody's first automated test passes on the first attempt, and the reason is almost never the application. It's the base URL pointing at an environment that redirects. It's the sign-in step hitting an SSO provider the sandbox can't reach. It's a credential that was never added, so the test types an empty string. It's an app that isn't reachable from the internet at all and needs a tunnel to a machine that can see it. It's a viewport where the nav collapses into a menu the test doesn't know to open.

Every one of these is worth finding. You just want to find them on a test where you already know what the app-side answer should be. Debugging "why can't the runner see my staging site" is a twenty-minute job. Debugging it while also wondering whether the customer record committed is a bad afternoon.

Get the boring layer right first: the environment resolves, sign-in works, variables and secrets are in place, the viewport is what you meant. Then the failures you see afterwards are about your app.

Read-only tests run where writes can't

The suite that catches the most bugs is the one that runs the most often, and read-only checks are the ones you're allowed to run everywhere.

They go against production right after a deploy, which is the single highest-value moment in the whole cycle. They go against every preview environment, including ones that ship with no seed data and no write permissions. They run on every pull request without a queue, because nothing they do can interfere with anything else running at the same time. You can crank up execution parallelism without thinking about it.

A write-heavy suite is welcome in exactly one place: an environment you're allowed to dirty. That's a real constraint on how much value it can deliver, and it's a reason to build the read-only spine first rather than a reason to skip writes forever.

One thing that's specific to generating tests with AI

Generation drives your actual application. The agent opens the site URL you gave it, works through the behavior you described, and verifies the result by running the test against the live app before it hands anything back.

Which means generating a "delete the customer" feature deletes customers. Not once, either. The agent may explore a path several times, and if you edit the Gherkin and regenerate, it goes again. In TestVibe the intent is the source of truth and the code is disposable, so regenerating is the normal way to change a test. That's a comfortable loop when the feature reads, and something you want to think about before you point it at a database you care about.

Read-only features are safe to iterate on. Point them anywhere, regenerate as many times as you like, and the worst case is a wasted generation. Save the write flows for a target where an extra hundred records is a shrug.

What the first ten features look like

Roughly in order:

  1. The entry point renders. Your post-deploy smoke test, and the one you'll run most.
  2. Sign in, and land somewhere real. Everything else depends on this, so it's worth having as its own feature rather than buried in a Background.
  3. Signed-out access is refused. Open a deep link to a protected page in a fresh session and land on sign-in. Cheap, and it catches an entire class of authorization regression.
  4. Every top-level nav destination opens its own page. One scenario per destination.
  5. A list view has rows. The first check that your data layer is alive at all.
  6. A detail view of a record you know is stable. Pick seed data, not whatever's newest.
  7. Search or filter returns something sensible. Read-only, and it exercises query paths that plain navigation never touches.
  8. The empty state and the 404. Filter for something that can't exist; deep-link an ID that isn't there. These break constantly and nobody tests them.
  9. One narrow viewport pass over the nav and one core page. Mobile layout breaks on its own schedule.
  10. The signed-in header shows the right identity. Sounds trivial, fails loudly the day session handling changes.

Two caveats on that list.

Sign-in is the one item that isn't strictly read-only, since it creates a session and some apps throttle repeated attempts from the same address. It's still worth doing early because it's a prerequisite for everything else. Just be aware that a burst of parallel sign-ins is one of the few ways a "read-only" suite can annoy the app under test.

And a read-only test still needs a real assertion. This is the trap:

Scenario: The reports page opens
  Given I am signed in
  When I open Reports from the main navigation
  Then the Reports page is displayed

"Is displayed" is doing no work. That scenario passes against an empty shell, a spinner that never resolves, and an error state rendered inside the right layout. It fails only when the server is down, which you already monitor. Anchor on something only that page can produce:

Scenario: The reports page opens with its filters and data
  Given I am signed in
  When I open Reports from the main navigation
  Then I see the heading "Reports"
  And I see the date range filter
  And the reports table has at least one row

Same read-only cost, and now it catches the failure you actually care about. There's more on this in web-first assertions and writing test descriptions that generate well.

When to start writing

You're ready for the first write flow when all of these are true:

That's a checklist, not a project. Most teams can satisfy it in an afternoon once the read-only spine is running, and testing features that write data covers the authoring patterns in detail.

Don't stay here

A suite that only reads is a suite that stays green while checkout is broken. The read-only phase is day one, not quarter one.

Once the plumbing holds, order the write flows by consequence and add them one at a time. The flow that takes money first. Sign-up next, since it's the other one where failure is invisible to you and total for the user. Then the destructive operations, delete and cancel and refund, last, because those are the ones where a badly scoped locator does real damage to a real environment.

The point of starting read-only isn't caution. It's that by the time you write your first create test, you want every failure it produces to mean something.

TestVibe is in early access. Get early access and the first ten features above are an afternoon's work: describe each one in plain English, generate, run, and watch a suite come up green for reasons you can trust.

Early access

Ready for tests that write themselves?