Can CeylanVienna-based, globally curious.
Learn/Testing

Test what the toast cannot prove

A form can say Saved while the database holds stale coordinates, missing fields, or old cache data. The fix is to make E2E tests verify the saved record, not just the success message.

2026-05-15·3 min read·intermediate

What happened

An event form looked healthy from the outside. The user changed fields, clicked save, and saw a success toast.

But the saved data was not trustworthy. Address changes could keep the previous map coordinates. The edit form accepted weak input. The create form had validation state that could drift from the data being submitted. The test suite did not catch it because it mostly checked that the dialog closed or that a success screen appeared.

That is the dangerous part: the UI told the truth about the request finishing, not about the data being correct.

Root cause

The tests were proving the wrong contract.

They asserted:

  • the form could be filled
  • the save button could be clicked
  • the success state appeared
  • the dialog closed
  • the updated title was visible somewhere on the page

Those are useful smoke checks, but they are not persistence checks.

For a real create/edit flow, the contract is bigger:

  • the browser selected the intended autocomplete result
  • the payload included the derived fields
  • the server accepted only valid input
  • the database stored the intended values
  • the next read returns those values
  • stale client cache did not hide a failed save

If the E2E test stops at the toast, a broken save path can still pass.

Why it was non-obvious

Forms often have derived state.

An address field might populate city, postal code, region, latitude, and longitude. A date field might generate opening-hour rows. A login dialog might resume a draft submit. An edit form might start with existing coordinates, then accidentally keep them after the address text changes.

The visible input is only one layer. The meaningful saved record is the combination of typed fields, derived fields, validation, API behavior, and refetch behavior.

That is why "I saw the new text on the card" can be misleading. The card may be optimistic, partially refreshed, or showing only one field while the broken field remains hidden.

The better E2E rule

For important forms, every save test should have a readback assertion.

After create:

await page.getByRole("button", { name: /create/i }).click();
await expect(page.getByRole("heading", { name: /created/i })).toBeVisible();

const response = await page.request.get("/api/items/mine");
const records = await response.json();
const saved = records.find((item) => item.name === uniqueName);

expect(saved.address).toBe("Selected Street 1");
expect(saved.latitude).toBeCloseTo(48.2, 4);
expect(saved.openingHours).toContain("09:00-18:00");

After edit:

await dialog.getByRole("button", { name: /save/i }).click();
await expect(dialog).not.toBeVisible();

const response = await page.request.get(`/api/items/${id}`);
const saved = await response.json();

expect(saved.name).toBe(updatedName);
expect(saved.address).toBe(updatedAddress);
expect(saved.longitude).toBeCloseTo(expectedLongitude, 4);

The important move is not the exact API path. It is the discipline: test the persisted record after the UI says success.

Make external services deterministic

Autocomplete and geocoding are classic sources of flaky tests. Do not depend on a live map provider in a form-save regression test.

Mock the search endpoint with a known result:

await page.route("**/api/geocode/search?**", async (route) => {
  await route.fulfill({
    json: [{
      display_name: "Selected Street 1, City",
      lat: "48.2000",
      lon: "16.3000",
      address: { road: "Selected Street", house_number: "1", city: "City" },
    }],
  });
});

Now the test verifies your app logic: dropdown selection, derived fields, payload, persistence, and readback.

Reusable rule

A toast proves that code reached a happy branch. It does not prove that the right data survived the round trip.

For any create/edit form with derived fields, cache, or autocomplete, write at least one E2E test that:

  • uses deterministic external-service mocks
  • submits through the real UI
  • reads back through the API
  • asserts the fields users cannot easily see

The hidden fields are where save bugs like to live.

More like this, straight to your inbox.

I write about Testing and a handful of other things I actually care about. No schedule, no filler. Just when I have something worth saying.

If this raised a question, I'd be happy to talk about it.

Find me →
← Back to Learn