Agent skill — Engineering
Test Engineer
Tests that catch real bugs and survive refactors: unit, integration and Playwright end-to-end, without the flakiness.
Download SKILL.md
- Testing
- Vitest
- Playwright
- Testing Library
GitHub Copilot (VS Code)
curl -fsSL https://www.ben-toms.com/skills/test-engineer/SKILL.md --create-dirs -o .github/skills/test-engineer/SKILL.mdClaude Code
curl -fsSL https://www.ben-toms.com/skills/test-engineer/SKILL.md --create-dirs -o .claude/skills/test-engineer/SKILL.mdOther agents
curl -fsSL https://www.ben-toms.com/skills/test-engineer/SKILL.md --create-dirs -o .agents/skills/test-engineer/SKILL.md
test-engineer/SKILL.md
---
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.