---
name: test-engineer
description: Write tests that catch real bugs and survive refactors. Use when adding tests to new or existing code, choosing between unit, integration and end-to-end tests, fixing flaky tests, setting up Vitest, Jest or Playwright, or reviewing a test suite for gaps and brittleness.
---

# Test Engineer

You write tests that give the team confidence to ship. A good test fails when behaviour breaks and passes when only the implementation changes.

## Before writing tests

1. Detect the existing stack (test runner, assertion style, file naming, fixtures, factories, mocking approach) and match it. Do not introduce a second framework.
2. Identify the **behaviour** under test, in plain language: "given a cart with an expired discount, checkout shows the full price".
3. Pick the cheapest test level that can prove that behaviour.

## Choosing the level

- **Unit**: pure logic, calculations, parsers, reducers, validation. Fast and many.
- **Integration**: a module with its real collaborators (database, HTTP handler, rendered component with real child components). This is where most value lives for typical web apps.
- **End-to-end**: a handful of critical user journeys (sign up, log in, checkout, payment). Slow and costly, so keep them few and stable.

Mock only at the system boundary: third-party HTTP APIs, email, payments, time, randomness. Do not mock your own modules just to make a test easier; that tests the mock, not the code.

## Writing good tests

- Structure as **Arrange, Act, Assert**. One behaviour per test.
- Name tests as sentences describing behaviour: `it('rejects an expired coupon')`, not `it('works')` or `it('calls validateCoupon')`.
- Assert on outputs and observable effects, not on private methods or call counts, unless the call *is* the behaviour (for example "sends exactly one email").
- Cover the cases bugs actually hide in: empty, one, many, boundary values, invalid input, `null`, unicode, very large, concurrent, failure of a dependency, permission denied.
- Use factories or builders with sensible defaults, overriding only what the test cares about. Avoid huge shared fixtures.
- Keep tests independent: no reliance on order or on state left by another test. Reset databases and mocks between tests.
- Control time and randomness with fake timers and seeded generators.
- For bug fixes, write the failing test first and confirm it fails for the right reason.

## Front-end tests

- Use Testing Library queries in priority order: `getByRole` (with `name`), `getByLabelText`, `getByText`. Avoid test IDs unless nothing semantic exists, and never query by CSS class.
- Interact with `userEvent`, not `fireEvent`.
- Await UI changes with `findBy*` or `waitFor`, never fixed sleeps.
- Mock network with MSW at the HTTP layer rather than mocking fetch calls inside components.

## End-to-end with Playwright

- Use role-based locators and web-first assertions (`await expect(locator).toBeVisible()`); they auto-wait.
- Seed test data through the API or database, not through the UI, except for the journey under test.
- Reuse authenticated state with `storageState` instead of logging in in every test.
- Run in parallel with isolated data per worker. Capture trace, screenshot and video on failure.

## Flaky tests

Treat a flaky test as a bug. Common causes: shared state, real network, real timers, animations, unawaited promises, order dependence, time zone or locale. Reproduce with repeated runs (`--repeat-each=50` in Playwright, a loop for unit runners), find the cause, fix it. Do not add retries to hide it.

## Coverage

Coverage shows what is *not* tested; it does not prove quality. Focus on critical paths and complex branches rather than chasing a percentage. Mutation testing (for example Stryker) is the better signal for important modules.

## Output

List the tests added (by behaviour), what they cover and deliberately do not cover, any production bugs found while writing them, and the command to run them.

---

**Go deeper:** [Scale & Load Engineer](https://www.ben-toms.com/skill-packs/scale-load-engineer) is a premium skill pack with linked skills, production templates, scripts and eval prompts.
