End-to-end (E2E) tests simulate a real user’s journey through your website—clicking buttons, filling forms, navigating pages—to verify that the entire system works together. This guide walks you through writing your first E2E tests using Playwright, a popular open-source tool, but the principles apply to Cypress, Selenium, or any other framework. You’ll learn how to set up the environment, write a test, run it, and debug failures.
What You’ll Need
- Node.js (version 16 or later) installed on your machine
- A code editor (VS Code recommended)
- Basic knowledge of JavaScript or TypeScript
- A test website (you can use a local dev server or a public demo site)
- Command line / terminal access
Step-by-Step
- Set up a new project
Create a folder for your tests and initialize it:mkdir e2e-tests && cd e2e-teststhennpm init -y. This creates apackage.jsonfile. - Install Playwright
Runnpm install -D @playwright/test. Then install the browsers:npx playwright install. This downloads Chromium, Firefox, and WebKit so you can test across browsers. - Create a test file
Create a folder namedtestsand inside it a fileexample.spec.js. Playwright uses the.spec.jsextension by default. - Write your first test
Open the file and add:const { test, expect } = require('@playwright/test'); test('homepage has title', async ({ page }) => { await page.goto('https://example.com'); await expect(page).toHaveTitle(/Example Domain/); });This test navigates to the site and checks that the page title matches a pattern.
- Run the test
In the terminal, runnpx playwright test. Playwright will run the test in headless mode (no browser window) and report pass/fail. To see the browser, use--headed. - Test a user interaction
Let’s test a login form. Add a second test:test('login flow', async ({ page }) => { await page.goto('https://example.com/login'); await page.fill('#username', 'testuser'); await page.fill('#password', 'password123'); await page.click('button[type="submit"]'); await expect(page).toHaveURL(/dashboard/); });Replace the selectors with those on your site. Use the browser’s DevTools to inspect elements and get reliable selectors (prefer data-testid attributes if available).
- Use test fixtures for setup
If your test needs a logged-in state, usetest.beforeEachto navigate and log in before each test. For example:test.beforeEach(async ({ page }) => { await page.goto('https://example.com/login'); await page.fill('#username', 'testuser'); await page.fill('#password', 'password123'); await page.click('button[type="submit"]'); });This keeps tests independent and avoids repeating code.
- Add assertions for key elements
After an action, check that the expected result appears. Useawait expect(page.locator('.success-message')).toBeVisible()orawait expect(page.locator('text=Welcome')).toBeVisible(). Assertions are crucial—they decide pass/fail. - Run tests in multiple browsers
Playwright runs tests in all configured browsers by default. You can specify a browser with--project=chromium. To change default browsers, editplaywright.config.js(create it if missing) and setprojects. - Use trace viewer for debugging
When a test fails, Playwright generates a trace file. Runnpx playwright show-traceto open the trace viewer, which shows screenshots, network requests, and console logs at each step.
Common Mistakes
- Using brittle selectors – Relying on CSS classes that change often or index-based selectors like
div:nth-child(2)breaks tests quickly. Use stable attributes likedata-testidor ARIA roles. - Not waiting for elements – Using
page.click()without waiting for the element to be ready can cause flaky tests. Playwright automatically waits for actionability, but if you usepage.waitForTimeout()(which is discouraged), you introduce fixed delays that slow tests and cause failures. - Testing implementation details – Asserting on internal CSS classes or DOM structure makes tests fragile. Test user-visible behavior, like text, URLs, and visible elements.
- Not isolating tests – If tests share state (e.g., a user already logged in), they can interfere. Use
beforeEachto reset state or create fresh data. - Ignoring test failures – When a test fails, don’t just re-run; investigate why. Use the trace viewer and fix the root cause (either the test or the app).
Tips
- Start small – Begin with the most critical user journeys: login, search, add to cart, checkout. You can expand later.
- Use data-testid attributes – Add
data-testidto elements that are important for tests. This decouples tests from CSS changes. - Run tests in CI – Integrate E2E tests into your continuous integration pipeline so they run on every commit. Playwright provides GitHub Actions examples.
- Keep tests independent – Each test should be able to run alone and in any order. Avoid dependencies between tests.
- Use environment variables – Store URLs and credentials in environment variables or a
.envfile to avoid hardcoding. - Parallelize – Playwright runs tests in parallel by default. Use multiple workers to speed up execution, but be aware of shared resources.
Troubleshooting
If your tests fail or behave unexpectedly, here’s what to check:
- Test times out – Increase the timeout in the config or per test:
test.setTimeout(60000). Also check if the page is loading slowly or if a network request hangs. - Element not found – Use the Playwright inspector to see what’s on the page. Run
npx playwright codegento open a browser and generate selectors interactively. Verify your selector matches exactly. - Test passes locally but fails in CI – Differences in browser versions, screen size, or network speed can cause issues. Use the same browser configuration in CI, and consider using a fixed viewport size in your tests.
- Flaky tests (pass sometimes, fail sometimes) – This usually indicates a race condition. Add explicit waits for the element to be visible or enabled instead of relying on timeouts. Use
expect(locator).toBeVisible()which auto-waits. - Can’t install browsers – If
npx playwright installfails, check your internet connection and disk space. You can install a specific browser:npx playwright install chromium. - Tests are slow – Use
--workers=4to run more in parallel. Also, avoid usingpage.waitForTimeout(); instead, wait for specific conditions.
Frequently Asked Questions
What is the difference between E2E tests and unit tests?
Unit tests check individual functions or components in isolation, while E2E tests verify the entire application flow from the user’s perspective, including the UI, backend, and database.
How many E2E tests should I write?
Focus on the critical user journeys that, if broken, would prevent users from completing core tasks. Quality over quantity; 10-20 well-chosen E2E tests are often more valuable than 100 redundant ones.
Can I run E2E tests on a staging server?
Yes, it’s common to run E2E tests against a staging environment that mirrors production. Ensure the test data is predictable and doesn’t affect real user data.
How do I handle authentication in E2E tests?
You can log in through the UI in beforeEach, or use Playwright’s storageState to save and reuse a logged-in session. The latter is faster and more reliable.
What if my website uses iframes or pop-ups?
Playwright supports iframes with page.frameLocator() and pop-ups with page.waitForEvent('popup'). Handle these elements explicitly in your tests.
Do I need to test on all browsers?
It’s best to test on the browsers your users actually use. Playwright makes it easy to run tests on Chromium, Firefox, and WebKit. If you have limited time, start with Chromium and add others later.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It