Unit vs integration vs end-to-end testing
JavaScriptThe short answer
Unit tests test individual functions or components in isolation. Integration tests test how multiple parts work together. End-to-end (E2E) tests simulate real user behavior across the entire application. Each type catches different kinds of bugs, and a good test strategy uses all three.
Unit tests
Unit tests verify that a single, small piece of code works correctly in isolation. Dependencies are usually mocked.
// Function to testfunction formatPrice(cents) { return `$${(cents / 100).toFixed(2)}`;}// Unit testtest('formats cents to dollar string', () => { expect(formatPrice(999)).toBe('$9.99'); expect(formatPrice(0)).toBe('$0.00'); expect(formatPrice(10050)).toBe('$100.50');});For React components:
test('renders button with correct text', () => { render(<Button>Submit</Button>); expect(screen.getByText('Submit')).toBeInTheDocument();});Characteristics:
- Fast to run (milliseconds)
- Easy to write and maintain
- Test one thing at a time
- Dependencies are mocked
Integration tests
Integration tests verify that multiple units work together correctly. They test the interaction between components, modules, or services.
test('submitting the form adds a todo', () => { render(<TodoApp />); const input = screen.getByPlaceholderText('Add a todo'); const button = screen.getByText('Add'); fireEvent.change(input, { target: { value: 'Buy milk' }, }); fireEvent.click(button); expect(screen.getByText('Buy milk')).toBeInTheDocument();});This test covers the input, the button, the state management, and the list rendering — all working together.
Characteristics:
- Slower than unit tests but faster than E2E
- Test the connections between parts
- May use real dependencies instead of mocks
- Catch bugs that unit tests miss
End-to-end (E2E) tests
E2E tests simulate a real user using the application. They open a real browser, click buttons, fill forms, and verify the results.
// Playwright exampletest('user can log in and see dashboard', async ({ page,}) => { await page.goto('/login'); await page.fill('#email', 'user@example.com'); await page.fill('#password', 'password123'); await page.click('button[type="submit"]'); await expect(page).toHaveURL('/dashboard'); await expect(page.locator('h1')).toContainText('Welcome');});Characteristics:
- Slowest to run (seconds to minutes)
- Most realistic — tests the entire system
- Hardest to maintain (brittle to UI changes)
- Catches bugs that other tests cannot (network issues, browser behavior)
The testing pyramid
The recommended approach is shaped like a pyramid:
/ E2E \ ← Few, slow, expensive /----------\ / Integration \ ← Some, moderate /----------------\ / Unit Tests \ ← Many, fast, cheap- Many unit tests — they are fast and cheap, catch most logic bugs
- Some integration tests — they catch interaction bugs
- Few E2E tests — they catch system-level bugs but are slow and brittle
Common tools
| Type | Tools |
|---|---|
| Unit | Jest, Vitest |
| Integration | React Testing Library, Jest |
| E2E | Playwright, Cypress |
Interview Tip
Explain each type with a simple example, then describe the testing pyramid. The key insight is that each type catches different bugs and has different tradeoffs (speed vs realism). If you can mention specific tools you have used, that shows practical experience. Most interviewers want to hear that you prefer React Testing Library for component tests because it tests behavior, not implementation details.
Why interviewers ask this
Testing shows engineering maturity. Interviewers want to see if you know the different types of tests, when to use each, and how to balance the tradeoffs. A candidate who can explain the testing pyramid and give examples for each level shows they think about code quality and reliability.