qa-engineering-best-practices — GitHub Copilot rule: QA and testing
A GitHub Copilot instruction file covering test strategy, the test pyramid, naming, assertions and bug reports. Useful when Copilot writes or reviews tests in your repo.
.github/instructions/qa-engineering-best-practices.instructions.mdWhat qa-engineering-best-practices does
This is an instructions file Copilot picks up while working on test code. It sets out core testing principles: write tests alongside the code, verify one behaviour per test, keep tests deterministic and independent of each other. Separate sections cover the test pyramid with its split between unit, integration and E2E layers, Given/When/Then naming, and using specific matchers instead of truthiness checks.
There are blocks on test data and mocking: factories rather than hardcoded objects, state isolation between tests, and mocking at the boundary — HTTP clients, DB adapters, message queues. Then come API testing (status codes, response schemas, auth paths, boundary values), UI and E2E (accessible selectors, explicit waits instead of sleep, screenshots on failure), performance testing and CI integration. The file closes with a seven-point bug report standard and coverage guidance.
The rule applies to every file, since it ships with applyTo: '**'. That means the instructions shape any task where Copilot touches tests, including ones where you asked for something else entirely. If your project already has its own testing conventions, they will fight with this file, so narrow applyTo to your test directories first. Installing it is just copying the file into .github/instructions/.
What it helps with
- settle on one test naming convention across the team
- rebalance tests across the pyramid instead of leaning on E2E
- swap truthiness checks for specific matchers
- write a bug report someone can reproduce on the first try
- remove
sleepcalls and flakiness from UI tests
Example prompts for the agent
- write unit tests for this module following qa-engineering-best-practices
- review my tests against the test pyramid and tell me what belongs in integration
- draft a bug report from this failure: title, environment, steps, expected and actual result
How to install qa-engineering-best-practices
Manually: put the files in place
.github/instructions/qa-engineering-best-practices.instructions.mdThe applyTo field sets which files it applies to
Manually: put the files in place
.claude/rules/qa-engineering-best-practices.mdThe paths frontmatter field limits the files: move applyTo from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.cursor/rules/qa-engineering-best-practices.mdcFrontmatter: description, globs, alwaysApply. Move applyTo from the source into globs, or the rule will not attach on its own
Manually: put the files in place
.devin/rules/qa-engineering-best-practices.mdManually: put the files in place
.clinerules/qa-engineering-best-practices.mdStep by step, with commands for each platform: AGENTS.md vs CLAUDE.md: what they are and which one to use
Questions about qa-engineering-best-practices
How do I install qa-engineering-best-practices in GitHub Copilot?
Copy the file to .github/instructions/qa-engineering-best-practices.instructions.md in your repository root. Copilot reads instructions from that folder.
What does this rule do?
It gives Copilot a set of QA instructions: testing principles, the test pyramid, naming, assertions, test data and mocking, API and E2E testing, bug reports and coverage.
Which files does it apply to?
It declares applyTo: '**', so the instructions affect every file in the project. If that gets in the way, narrow the pattern to your test directory.
Source text
qa-engineering-best-practices.instructions.md · MIT
---
applyTo: '**'
description: 'Comprehensive QA engineering best practices covering test strategy, test pyramid, naming conventions, assertion patterns, bug reporting, and automation guidelines for modern software projects.'
---
# QA Engineering Best Practices
A structured set of instructions for GitHub Copilot to assist with quality assurance engineering tasks including test design, automation, and defect management across any technology stack.
---
## Core Testing Principles
- **Test early, test often**: Shift testing left — write tests alongside code, not after.
- **Test one thing at a time**: Each test case should verify a single behaviour or assertion.
- **Tests are first-class code**: Apply the same readability, naming, and refactoring standards to test code as to production code.
- **Fail fast**: Tests should produce clear, actionable failures that point directly to the broken behaviour.
- **Deterministic tests**: Tests must produce the same result on every run. Eliminate randomness, timing dependencies, and shared mutable state.
- **Independent tests**: No test should depend on another test's side effects. Tests must be runnable in any order.
---
## Test Pyramid
Follow the test pyramid to balance coverage, speed, and maintenance cost:
| Layer | Scope | Quantity | Speed |
|-------|-------|----------|-------|
| Unit | Single function / class | Many (60–70 %) | Milliseconds |
| Integration | Module boundaries, DB, API contracts | Moderate (20–30 %) | Seconds |
| End-to-End | Full user journey across UI + backend | Few (5–10 %) | Minutes |
- Prefer unit tests for business logic and edge cases.
- Use integration tests to validate contracts between services and external dependencies.
- Reserve end-to-end tests for critical user paths and smoke suites.
---
## Test Naming Conventions
Use the **Given / When / Then** (GWT) or **should_doX_whenY** pattern consistently.
```
// Good – describes scenario, action, expected result
test('should return 404 when product id does not exist')
test('given an expired token, when the user calls /me, then it returns 401')
// Bad – vague, implementation-focused
test('test1')
test('check user')
```
- Group related tests in `describe` / `context` blocks named after the unit under test.
- Use `it` or `test` for individual cases.
- Test names must be readable as standalone sentences.
---
## Assertion Best Practices
- **One logical assertion per test** where practical; avoid asserting multiple unrelated things.
- Use **specific matchers** over equality checks (`toContain`, `toBeGreaterThan`, `toMatchObject`).
- Always assert the **exact expected value**, not just truthiness (`expect(result).toBe(42)` not `expect(result).toBeTruthy()`).
- For exception testing, assert both the exception type and message.
- Prefer **positive assertions** over negative ones when testing the happy path.
```typescript
// Good
expect(response.status).toBe(200);
expect(response.body.items).toHaveLength(3);
// Avoid
expect(response).toBeTruthy();
expect(response.body).not.toBeNull();
```
---
## Test Data Management
- Use **factories or builders** to create test data — avoid hardcoding raw objects in every test.
- Keep test data **minimal**: only include fields relevant to the test.
- Use **unique identifiers** per test run to avoid collision in shared environments.
- Never use production data or PII in tests.
- Reset or isolate state between tests (in-memory DB, transactions rolled back, mocked dependencies).
---
## Mocking and Stubbing Guidelines
- Mock **at the boundary** (HTTP clients, DB adapters, message queues) — not deep inside business logic.
- Prefer **real implementations** for pure functions and simple value objects.
- Stubs return controlled data; mocks additionally verify interactions — choose the right tool.
- Reset all mocks between tests to prevent state leakage.
- Document why a dependency is mocked if the reason is non-obvious.
---
## API Testing
- Validate **status code**, **response schema**, **headers**, and **response time** for every endpoint.
- Test all **HTTP methods** the endpoint exposes (GET, POST, PUT, PATCH, DELETE).
- Cover **authentication and authorisation** paths: valid token, expired token, missing token, wrong role.
- Test **boundary values** for inputs: empty string, null, max length, special characters, Unicode.
- Validate **error response bodies** follow a consistent schema.
- Assert **idempotency** for PUT and DELETE operations.
---
## UI / End-to-End Testing
- Target **user-visible behaviour**, not implementation details (avoid asserting CSS classes or internal state).
- Use **accessible selectors** in order of preference: `role` → `label` → `test-id` → `text`.
- Avoid `sleep` / fixed waits; use **explicit waits** on element state (visible, enabled, network idle).
- Run E2E tests against a **stable, isolated environment** (not shared staging).
- Keep E2E scenarios **short and focused** — break long flows into smaller composable steps.
- Capture **screenshots and traces** on failure for easier debugging.
---
## Performance Testing
- Define **SLOs** (Service Level Objectives) before writing performance tests: target latency p50/p95/p99, throughput, error rate.
- Include **ramp-up**, **steady state**, and **ramp-down** phases in load tests.
- Test under **realistic data volumes** — synthetic tests with empty DBs are not representative.
- Track results over time to detect **performance regressions**.
- Distinguish between **load testing** (expected traffic), **stress testing** (beyond capacity), and **soak testing** (sustained load over time).
---
## Bug Reporting Standards
A good bug report includes:
1. **Title**: concise, specific — include component, action, and symptom (`[Checkout] Order total is incorrect when coupon is applied`).
2. **Environment**: OS, browser/runtime version, deployment environment.
3. **Steps to reproduce**: numbered, minimal, deterministic.
4. **Expected result**: what should happen.
5. **Actual result**: what actually happens, including error messages and stack traces.
6. **Severity**: Critical / High / Medium / Low (defined by business impact).
7. **Attachments**: screenshots, logs, network traces, test IDs.
---
## Test Coverage Guidelines
- Aim for **meaningful coverage**, not a percentage target — 100 % line coverage with trivial tests is worthless.
- Prioritise coverage for **critical paths**, **complex logic**, and **previously buggy areas**.
- Track **branch coverage** and **mutation scores** alongside line coverage.
- Use coverage reports to find untested **edge cases**, not to game metrics.
---
## CI/CD Integration
- Tests must pass in CI before any merge to main/trunk — no exceptions.
- Run **fast tests** (unit, lint) on every commit; run **slow tests** (integration, E2E) on PR merge or nightly.
- Make test failures **visible and actionable** in CI output — include test name, failure reason, and relevant logs.
- Archive **test reports and artefacts** (JUnit XML, coverage HTML, traces) as CI build artefacts.
- Configure **flaky test detection**: auto-retry once, flag as flaky after repeated inconsistency.
---
## Test Review Checklist
Before approving a PR that changes tests:
- [ ] New behaviour is covered by tests at the appropriate pyramid level.
- [ ] Tests are named clearly and follow the project convention.
- [ ] No `sleep`, `Thread.Sleep`, or arbitrary timeouts.
- [ ] Mocks are reset after each test.
- [ ] No hardcoded environment-specific values (URLs, credentials).
- [ ] Tests are independent and can run in isolation.
- [ ] Test code is readable without needing to read the implementation.
© github · MIT · github/awesome-copilot