Skip to content
intermediate Phase 13 · Testing Angular Applications

End-to-End Testing

Cover real user journeys through routes, forms, and APIs.

45m
0 problems
Topic Progress 0%

E2E Testing Overview

End-to-end tests drive a real browser to cover flows that cross components, routes, and HTTP.

E2E vs Unit Tests

Feature Unit Tests E2E Tests
Scope Individual units Full user journeys
Speed Fast Slower
Browser Headless (Karma) Real browser
Network Mocked Real or mocked
Confidence Low (isolated) High (integration)
Cost Cheap Expensive

E2E Test Frameworks

Framework Status
Playwright Recommended — modern, fast, reliable
Cypress Popular — great developer experience
Protractor Deprecated — do not use for new projects

Playwright Setup

npm install @playwright/test --save-dev
npx playwright install
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  use: {
    baseURL: 'http://localhost:4200',
    headless: true,
  },
});

Basic E2E Test

// e2e/app.spec.ts
import { test, expect } from '@playwright/test';

test('should display the home page', async ({ page }) => {
  await page.goto('/');
  await expect(page.locator('h1')).toHaveText('Welcome');
});

test('should navigate to about page', async ({ page }) => {
  await page.goto('/');
  await page.click('a[href="/about"]');
  await expect(page.locator('h1')).toHaveText('About');
});

E2E Testing Patterns

E2E tests should use stable selectors and cover critical user flows.

Using data-testid

Stable data-testid attributes make selectors resilient to styling and copy changes:

<!-- Component template -->
<button data-testid="submit-button">Submit</button>
<input data-testid="email-input" type="email">
// E2E test
await page.fill('[data-testid="email-input"]', 'test@example.com');
await page.click('[data-testid="submit-button"]');

Testing a Complete Flow

test('user can create a todo', async ({ page }) => {
  await page.goto('/todos');

  // Fill the form
  await page.fill('[data-testid="todo-input"]', 'Learn E2E testing');
  await page.click('[data-testid="add-todo-button"]');

  // Verify the todo appears
  await expect(
    page.locator('[data-testid="todo-item"]')
  ).toContainText('Learn E2E testing');

  // Mark as done
  await page.click('[data-testid="todo-checkbox"]');
  await expect(
    page.locator('[data-testid="todo-item"]')
  ).toHaveClass(/completed/);
});

Running E2E in CI

# Run e2e tests
npx playwright test

# Run with UI mode (debugging)
px playwright test --ui

# Run in headed mode
npx playwright test --headed

# Generate HTML report
npx playwright show-report

E2E Best Practices

  1. Use data-testid — Resilient to CSS and text changes
  2. Test critical flows — Login, checkout, form submission
  3. Mock external APIs — Use Playwright's route interception
  4. Keep tests independent — Each test sets up its own state
  5. Run in CI — Catch regressions before they reach production

Quiz

1. What is the difference between unit tests and e2e tests?

Question 1 options

2. Which e2e framework is recommended for Angular?

Question 2 options

3. Why use data-testid attributes in e2e tests?

Question 3 options

Flashcards

Question

Unit vs e2e tests?

Answer

Unit: isolated units. E2E: full user journeys in real browser.

Question

Recommended e2e framework?

Answer

Playwright — modern, fast, reliable. Protractor is deprecated.

Question

Why use data-testid?

Answer

Stable selectors resilient to styling and copy changes.

Revision Notes

Key Takeaways

  • 1. E2E tests cover full user journeys in a real browser
  • 2. Playwright is the recommended framework
  • 3. data-testid provides stable selectors
  • 4. Run e2e in CI for regression detection

Interview Tips

  • • Explain the difference between unit and e2e tests
  • • Be ready to describe Playwright setup
  • • Mention data-testid for resilient selectors

Cheat Sheet

E2E Testing Cheat Sheet

Framework: Playwright (recommended)

Setup:

npm install @playwright/test --save-dev
npx playwright install

Basic test:

test('should work', async ({ page }) => {
  await page.goto('/');
  await expect(page.locator('h1')).toHaveText('Welcome');
});

Best practices:

  • Use data-testid for stable selectors
  • Test critical user flows
  • Mock external APIs
  • Run in CI

Continue Learning