/\
/e2e\ ← Few, slow, test full user journeys
/──────\
/integr. \ ← Some, test component interactions
/──────────\
/ unit tests \ ← Many, fast, test single units in isolation
/──────────────\
it("should <expected behavior> when <condition>")
it("should throw <error> if <invalid condition>")
it("should return <value> given <input>")
Mocking Strategy
// Mock external dependencies, not internal logic
jest.mock("../services/EmailService"); // External service
jest.mock("../repositories/UserRepository"); // Database layer
// Do NOT mock:
// - The unit under test itself
// - Simple utility functions
// - Pure functions with no side effects
Test File Organization
src/
users/
user.service.ts
user.service.test.ts ← Unit tests live next to the file
tests/
integration/
user-api.test.ts ← Integration tests in separate folder
e2e/
registration.test.ts ← E2E tests
Running and CI Integration
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Run specific file
npm test -- user.service.test.ts
# Watch mode during development
npm test -- --watch
Coverage Goals
Unit tests: aim for 80%+ line coverage of business logic
Do NOT chase 100% — test behavior, not implementation details
Always test: error handling branches, validation logic, data transformations
Agent Instructions
Read the source file completely before writing tests
Check the project for existing test patterns (look at existing .test.ts files)
Match the existing test framework (jest, vitest, mocha, etc.)
Run tests with execute_bash after writing to confirm they pass
If a test is hard to write, it's a signal the code needs refactoring
Add tests for the specific bug being fixed (regression tests)
Exploratory QA for Web Apps
Automated tests above verify known behavior. Exploratory QA finds the unknown
bugs by systematically driving a running app, gathering evidence, and reporting.
When to use
"Test / try this app", "check for bugs"
Verifying UI or API changes, reviewing a feature before production
Workflow (5 phases)
Plan — scope: which URL/feature, which browser, credentials/test data, what's out of scope.
Auth: invalid credentials, session timeout, direct access to protected pages while logged out, password reset, "remember me".
Speed/reliability: throttled network, double-click (duplicate submit), back button after submit, refresh mid-operation.
Rules
Snapshot after every meaningful step — no evidence, no bug.
Reproduction steps must work at least 3 times.
Don't guess — screenshot, observe, document.
Critical bugs first; stay scope-focused.
Also document what works (positive testing).
Cross-References
code-review — reviewing the diff behind a change before/after QA.
debugging — root-causing a bug once exploratory QA surfaces it.
1---2name: testing3description: Test writing SOP for unit, integration, and e2e tests (TDD) plus exploratory QA for web apps — find bugs, gather evidence, write reports.4---56# Testing SOP78## Test Pyramid9```10 /\11 /e2e\ ← Few, slow, test full user journeys12 /──────\13 /integr. \ ← Some, test component interactions14 /──────────\15 / unit tests \ ← Many, fast, test single units in isolation16 /──────────────\17```1819## What to Test — Behavioral Coverage Checklist2021For every function/module, think through:22- [ ] **Happy path**: normal inputs → expected output23- [ ] **Edge cases**: empty string, zero, null, undefined, empty array, max integer24- [ ] **Error cases**: invalid input, missing required field, network failure25- [ ] **Boundary values**: min/max allowed values, exact boundary, just over/under26- [ ] **Side effects**: does it correctly modify state, call dependencies?2728## Test Structure — Arrange, Act, Assert29```typescript30describe("UserService.createUser", () => {31 it("should hash the password before saving", async () => {32 // ARRANGE33 const mockRepo = { save: jest.fn().mockResolvedValue({ id: "1" }) };34 const service = new UserService(mockRepo);35 const plainPassword = "secret123";3637 // ACT38 await service.createUser({ email: "test@example.com", password: plainPassword });3940 // ASSERT41 const savedUser = mockRepo.save.mock.calls[0][0];42 expect(savedUser.password).not.toBe(plainPassword);43 expect(savedUser.password).toMatch(/^\$2[aby]\$/); // bcrypt hash44 });4546 it("should throw if email already exists", async () => {47 // ARRANGE48 const mockRepo = { save: jest.fn().mockRejectedValue(new DuplicateKeyError()) };49 const service = new UserService(mockRepo);5051 // ACT & ASSERT52 await expect(53 service.createUser({ email: "existing@example.com", password: "pass" })54 ).rejects.toThrow("Email already in use");55 });56});57```5859## Naming Tests60```61it("should <expected behavior> when <condition>")62it("should throw <error> if <invalid condition>")63it("should return <value> given <input>")64```6566## Mocking Strategy67```typescript68// Mock external dependencies, not internal logic69jest.mock("../services/EmailService"); // External service70jest.mock("../repositories/UserRepository"); // Database layer7172// Do NOT mock:73// - The unit under test itself74// - Simple utility functions75// - Pure functions with no side effects76```7778## Test File Organization79```80src/81 users/82 user.service.ts83 user.service.test.ts ← Unit tests live next to the file84tests/85 integration/86 user-api.test.ts ← Integration tests in separate folder87 e2e/88 registration.test.ts ← E2E tests89```9091## Running and CI Integration92```bash93# Run all tests94npm test9596# Run with coverage97npm test -- --coverage9899# Run specific file100npm test -- user.service.test.ts101102# Watch mode during development103npm test -- --watch104```105106## Coverage Goals107- Unit tests: aim for 80%+ line coverage of business logic108- Do NOT chase 100% — test behavior, not implementation details109- Always test: error handling branches, validation logic, data transformations110111## Agent Instructions1121. Read the source file completely before writing tests1132. Check the project for existing test patterns (look at existing .test.ts files)1143. Match the existing test framework (jest, vitest, mocha, etc.)1154. Run tests with execute_bash after writing to confirm they pass1165. If a test is hard to write, it's a signal the code needs refactoring1176. Add tests for the specific bug being fixed (regression tests)118119---120121# Exploratory QA for Web Apps122123Automated tests above verify known behavior. Exploratory QA finds the *unknown*124bugs by systematically driving a running app, gathering evidence, and reporting.125126## When to use127- "Test / try this app", "check for bugs"128- Verifying UI or API changes, reviewing a feature before production129130## Workflow (5 phases)1311321. **Plan** — scope: which URL/feature, which browser, credentials/test data, what's out of scope.1332. **Explore** — drive the app systematically:134 ```135 browser_navigate(url="https://app.example.com")136 browser_snapshot() # capture state (evidence)137 browser_click(selector="button[type=submit]")138 browser_type(selector="input[name=email]", text="test@example.com")139 browser_console() # JS errors / warnings140 ```141 Cover: every nav link/button, form submission (valid + invalid), error142 messages, load speed, responsive behavior, empty states.1433. **Collect evidence** — screenshot, exact reproduction steps, console output,144 environment (browser, URL, user state). No evidence → bug is invalid.1454. **Categorize** — severity 🔴 Critical / 🟠 High / 🟡 Medium / 🟢 Low; category146 Functional / UI-UX / Performance / Security / Accessibility.1475. **Report** — structured markdown:148 ```markdown149 # QA Test Report — [App]150 **Date / Scope / Environment / Total bugs (Critical: N, High: N, ...)**151152 ## 🔴 Critical153 ### BUG-001: [Title]154 - Steps / Expected / Actual / Evidence155 ## ✅ Working156 - [features tested and passing]157 ```158159## Useful exploratory patterns160- **Forms**: empty submit, 1000+ char input, special chars (`<script>`, `'`, `"`, `&`, `;`), invalid email, negative/zero, future/past dates.161- **Auth**: invalid credentials, session timeout, direct access to protected pages while logged out, password reset, "remember me".162- **Speed/reliability**: throttled network, double-click (duplicate submit), back button after submit, refresh mid-operation.163164## Rules1651. Snapshot after every meaningful step — no evidence, no bug.1662. Reproduction steps must work at least 3 times.1673. Don't guess — screenshot, observe, document.1684. Critical bugs first; stay scope-focused.1695. Also document what works (positive testing).170171## Cross-References172- `code-review` — reviewing the diff behind a change before/after QA.173- `debugging` — root-causing a bug once exploratory QA surfaces it.
Run npx skillmds@latest add furkangonel/testing in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Test writing SOP for unit, integration, and e2e tests (TDD) plus exploratory QA for web apps — find bugs, gather evidence, write reports. It is listed under Integrations & APIs on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
furkangonel (@furkangonel) published this skill. Their other Agent Skills are listed on their SkillMD profile.