A reading-list page can pass a screenshot review while losing a user's entry. The input looks correct, the Add button changes color and the spinner appears. Then a failed request clears the title without saving it. A useful test starts with that consequence, rather than checking whether the button has the expected CSS class.
For a first project, define one small feature: someone enters a book title, submits it and sees the saved title in a list. The title should remain available for correction if saving fails. This article uses an imagined reading-list application; the Developer Lab supplies a separate lesson-service simulation for practising response handling.
Write a behavior table before choosing a test runner
Describe the starting condition, the action and the visible result. Four cases are enough to expose several decisions that otherwise remain hidden:
- A valid title with a successful save appears once in the list.
- A blank or whitespace-only title receives a useful validation message.
- A server failure leaves the entered title available and displays a retry path.
- A slow save does not let repeated clicks accidentally submit several copies.
The last case needs a defined product rule. Disabling submission while a request is pending is one possible interface measure. A backend that handles duplicate creation safely needs its own contract and tests. A disabled button cannot guarantee that a retried network request will never create a duplicate.
Testing Library's guiding principles favor tests resembling how people use the page. That makes a label such as Book title and a button named Add book useful anchors. An assertion about a private component variable is less persuasive evidence that someone can actually add a book.
Put the check at the layer that can prove it
A small validation function can be tested without opening a browser. Give it an empty string, spaces and an ordinary title. An integration test can check that a backend rejects invalid input even when the frontend is bypassed. A browser test can check that the label, submission and saved result work together.
These checks overlap slightly because they prove different boundaries. Avoid writing dozens of tests that repeat the same string comparison without covering another risk. For a small personal page, a concise manual check may be sufficient; a feature accepting important writes deserves stronger evidence.
In Playwright, a user-facing browser test might begin like this in your own reading-list project:
await page.getByLabel('Book title').fill('River Maps');
await page.getByRole('button', { name: 'Add book' }).click();
await expect(page.getByRole('listitem').filter({
hasText: 'River Maps'
})).toHaveCount(1);
This snippet is a shape to adapt, not a complete runnable suite. It assumes an appropriate page, imports, test data and a controlled save response. The Playwright best-practices guide explains user-facing locators, isolated tests and controlled dependencies.
Control the failure instead of waiting for an outage
Use a fixture or a mock for a third-party response when checking your own interface. A live provider can change data, throttle requests or become unavailable. Those events should not determine whether your title-validation test passes. Keep a separate, carefully bounded integration check if the real connection itself needs verification.
Reset test data between cases. A test called adds a book should not depend on another test having logged in or deleted yesterday's entry. Shared accounts and leftover rows make failures hard to interpret. Fixed delays also make poor synchronization: wait for the relevant result or state, and set a sensible overall timeout.
For browser networking, remember that an HTTP error can still produce a fulfilled fetch promise. MDN's Fetch documentation explains checking the response status. Test the error message and preserved input, not merely that a catch block ran.
Practise with a response matrix
- Open the simulated HTTP workbench and inspect a successful lesson request.
- Try an invalid title for POST /lessons, then the available 401, 429 and 500 scenarios.
- For each result, write the message and next action your reading-list page would need.
- Implement one failure case in your own project and deliberately break its error handling.
The expected result is a test that fails when the title is lost and passes when the title remains available with a clear error. The lab does not run Playwright, prove authentication or save real records. It helps you choose cases; your application tests must prove their implementation.
When a test fails, read its first useful evidence before increasing retries. A changed label, bad fixture or real application regression needs a different repair. Keep the failure understandable enough that the next person can tell which one occurred.