Testing a site: what an end-to-end test is
Why:A site breaks quietly: a style was tweaked and the form stopped submitting. Afterwards you understand what a test is, how unit, integration and end-to-end (e2e) tests differ, you can read a Playwright example, and you can draw up a plan of scenarios for your project’s main action, with test data and no one else’s real data.
25 minutes
Read by speech synthesis (Qwen3-TTS); the voice is not a person’s.
An end-to-end (e2e) test is a program that walks a person’s path through a site in a real browser and compares the result with what is expected.
Why have a machine check the site
The first test of a site is you: you opened it, clicked, looked. On a small site, and the first time, that is enough. But every change can break something far away: someone renamed a class in the footer and the form at the top stopped submitting; a plugin was updated and a field lost its label. You check the place you edited and don’t check the rest. Other people find out before you do.
A test is a small program that does what a person would do and compares the result with what it should be. Its value isn’t that it is smarter but that it never tires and never forgets: you can run it on every change in a minute.
Tests come in different sizes.
- A unit test checks one small function apart from everything else. “The function that computes an order’s cost, given a 10 percent discount, returns 900 out of 1,000.” Fast, cheap, precise.
- An integration test checks how several parts work together. “The inquiry receiver puts an inquiry in the database, and it shows in the list.”
- An end-to-end test (e2e) checks a person’s whole path in a real browser. Open the home page, press “Book,” type a phone number, press “Send,” see “Thank you.” The closest to real life, and the slowest.
The idea comes from Mike Cohn, and it is well described in an article by Ham Vocke on Martin Fowler’s site. It is a pyramid: at the bottom many fast unit tests, higher up the integration tests, and at the very top a few end-to-end tests. The higher the floor, the more it costs to write and maintain, but the smaller the chance that everything is green while it is broken for a person. A site with no server logic of its own (a static one) may have no unit tests at all. Still, two or three end-to-end tests on the main action remain the most useful check of all.
Check yourself: how does an end-to-end test differ from a unit test?
A unit test checks one small part on its own, and an end-to-end test walks a person’s whole path in a real browser. So an end-to-end test is closer to reality, but slower and costlier to maintain.
What an end-to-end test looks like
One of the most widespread tools is Playwright (made by Microsoft). It starts a real browser (Chromium, Firefox or WebKit) and drives it from code. Here is a test of the main action:
import { test, expect } from '@playwright/test';
test('book a consultation', async ({ page }) => {
await page.goto('/');
await page.getByRole('link', { name: 'Book' }).click();
await page.getByLabel('Phone').fill('+1 000 000 0000');
await page.getByRole('button', { name: 'Send' }).click();
await expect(page.getByRole('status')).toContainText('Thank you');
}); Read it as a story. Open the page. Find the link named “Book” and click it. Find the field labeled “Phone” and type a number. Press the “Send” button. Make sure the status region contains the words “Thank you.” Notice that the test finds elements the way a person or a screen reader does, by role and label. That isn’t an accident but the advice of the tool’s own authors. Such a test is steady: a class can be renamed, while the label and the role stay. It also checks accessibility along the way: if a field lost its label, the test won’t find it, just as a screen reader wouldn’t.
Two properties make Playwright tests less brittle. First, auto-waiting: before each action it waits until the element appears, becomes visible and isn’t covered, so you needn’t insert a “wait a second” pause. Second, it opens a fresh clean browser every time, so tests don’t affect each other.
This textbook is checked the same way: it has end-to-end tests, as well as the accessibility and speed checks from the two previous lessons. All of it runs automatically on every change.
Try it in the diagram: break the site, choose how the test finds elements, and see what it notices.
The test walks the site’s main action the way a person does. Break the site in any way, choose how the test finds elements, and run it.
- Open the home page
- Find the link “Book” by role and name and click it
- Find the “Phone” field by its label and type a number
- Click the “Book” button (found by role and name)
- Wait for the message “Thank you” (role status)
Press “Run the test.”
Text alternative: the diagram runs an imaginary test of five steps. A test that finds elements by role and label notices a vanished field label, a covered button and a server error, and doesn’t react to a renamed class. A test by CSS class misses the vanished label and fails on a harmless class rename.
Check yourself: why is it better to find an element by role and label than by CSS class?
A class is a detail of styling. It can be renamed without changing anything visible, and the test fails for nothing. A role and a label are seen by both a person and a screen reader, so a test that finds elements by them also checks accessibility.
What to test on a site
You don’t need to test everything: every test has to be maintained. Start with the question “what can we absolutely not afford to break?” For a site that is almost always:
- The main action from the brief: book, buy, call, subscribe. The path from the home page to the result. This is the first and main test.
- An error and an empty form: what a person sees if they enter something wrong. A message next to the field, focus on the error.
- The main pages open (not a 404), and links between sections don’t lead nowhere.
- The keyboard: the form can be filled in and submitted without a mouse.
- Languages: the switcher leads to the right version, and the page’s
langmatches the text. - Consent and the policy are present beside the form (more on that later in this part).
- The phone: the main action on a narrow screen (375 pixels) works the same.
Tests don’t need to check other people’s services (does mail work behind the scenes), pixel-perfect matches, or small texts that you change often.
It helps to draw up the plan in words, before any code. Scenarios are customarily written as “Given, When, Then”: where we start, what the person does, what must happen. They are understandable to more than a programmer, and you can show them to a client: “here is what we check on every change.”
Tests and data
A test that submits a form creates a real inquiry. So:
- The data is made up. “Test,” a phone made of zeros, an email at
example.com(a domain reserved for examples). Real names and phone numbers of people aren’t used in tests. They are personal data, and you have no right to spend them on a check. - Test inquiries are marked, so they don’t reach real customers: a separate field, a service address or a separate test environment.
- A test doesn’t hit someone else’s live site. Check your own site on your own test environment or your own copy.
- The checks of forms and hidden bot traps (the next lesson) must not break because of tests: a test is a visitor too, and it needs a way through.
Flaky tests
The worst test is one that sometimes passes and sometimes doesn’t for no reason. It is called flaky. A test that can’t be trusted is worse than none: people stop noticing red, and a real breakage drowns. The causes are usually these: a pause instead of waiting, data shared between tests, the network, the clock. You cure the cause and don’t rerun until it is green.
A useful rule: a red test either finds a real breakage or shows that the test needs rewriting. Ignoring red is wrong in either case.
Where tests run
A test is useful when it runs without your memory. In the lesson on CI/CD you saw that a deploy system can run checks on every commit and refuse to deploy if anything is red. Site tests plug in exactly there. For a small project it is enough that they run on every push of changes. Manual checks can’t be replaced: a real phone, a screen reader and a human eye find what a test didn’t expect.
Practice
You are in the era of 2022: the site deploys automatically, and it is time to add a check to that pipeline. Draw up a plan of end-to-end tests for your project’s main action and finish a Playwright test.
The page keeps a table of scenarios and a block of code in the note. The check reads text: it makes sure the plan has the main action and the sections from the brief, and the code has a role, a label and a check of the result without pauses. It can’t run the test. You’ll run it for real when you connect Playwright to your own project: the instructions are in the sources.
What used to be different
In the 2000s a site was checked by the person who made it: opened in three browsers and “clicked around.” Later came tools like Selenium that could drive a browser but were slow and temperamental: tests failed on their own and people stopped trusting them. Modern tools (Playwright, Cypress) removed the main inconveniences: waiting is built in, browsers install with one command, and a test reads like a story. But the main rule is the same: few tests, on what must not break.
Practice
Preparing your page…
Test
Question 1 of 6
Cards
These cards go into your review once you finish the lesson.
What is the test pyramid?
Answer
Many fast unit tests, fewer integration tests and just a few end-to-end tests. The higher up, the costlier and slower, but the closer to what a person sees.
What is an end-to-end (e2e) test?
Answer
A program that walks a person’s path in a real browser (open, click, type) and compares the result with what is expected. For a site, first of all it is the main action from the brief.
How should a test find elements?
Answer
By role and label: getByRole, getByLabel, getByText. This is steady and checks accessibility along the way. Hooking onto CSS classes is brittle.
What is a flaky test?
Answer
A test that sometimes passes and sometimes fails on the same code. The causes: pauses instead of waiting, shared data, the network. Fix it; don’t just rerun it.
What data goes into tests?
Answer
Made-up data: “Test,” a phone made of zeros, example.com. Real personal data is not used in tests, and test inquiries are marked.
Sources
- Playwright: Installation and writing tests link only, in English
- Playwright: Best practices link only, in English
- The Practical Test Pyramid (Ham Vocke, martinfowler.com) link only, in English
- Automated testing (MDN) CC BY-SA, in English
- Software testing (Wikipedia) CC BY-SA, in English
Next
You now know: end-to-end testing.