Test components the way users use them with TestBed, then cover critical flows end-to-end in a real browser with Playwright.
Why: TestBed creates your component in a test sandbox — set its inputs, click its buttons, and assert on the rendered HTML, the way a user would experience it. Note: ng new sets the test runner up for you; run the suite with (ng test) in the terminal.
// ng new sets this up — run tests with (ng test)
import { TestBed } from '@angular/core/testing'
// The Counter component from the Signals lesson,
// saved as counter.ts in the same folder as this test
import { Counter } from './counter'
describe('Counter', () => {
it('increments when clicked', () => {
// Create the component in a test sandbox
const fixture = TestBed.createComponent(Counter)
fixture.componentRef.setInput('step', 2)
fixture.detectChanges() // render with the current state
const button: HTMLButtonElement =
fixture.nativeElement.querySelector('button')
expect(button.textContent).toContain('Count: 0')
// Interact the way a user would, then re-render and assert
button.click()
fixture.detectChanges()
expect(button.textContent).toContain('Count: 2')
})
})Why: end-to-end tests drive the real app in a real browser — routing, server, database, everything wired together. A handful of E2E tests over your critical flows catch what unit tests cannot.
$ pnpm create playwrightimport { test, expect } from '@playwright/test'
// Drives the Signup form from the Forms lesson — start the app
// first with (ng serve), then run the test
// page is a real browser tab that the test drives
test('user can sign up', async ({ page }) => {
await page.goto('http://localhost:4200/signup')
// Find elements the way a user would, then act on them
await page.getByPlaceholder('you@example.com').fill('ada@example.com')
await page.getByRole('button', { name: 'Sign up' }).click()
// Passes once the welcome message appears
await expect(page.getByText('Welcome, ada')).toBeVisible()
})