Skip to content
Back to skills

Test Plan Format

ASecurity

Test plan structure, naming conventions, edge case generation rules, and file saving conventions for QA test plans.

  • 5 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 28, 2026
developmentgoshellsqldockertestinggitapidatabase

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add AppVerk/av-marketplace --skill test-plan-format --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Plan Format?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Plan Format
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/appverk-test-plan-format/badge)](https://www.skillsdirectory.com/skills/appverk-test-plan-format)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-plan-format
description: Test plan structure, naming conventions, edge case generation rules, and file saving conventions for QA test plans.
---

# Test Plan Format

## File Conventions

- **Location:** `docs/testing/plans/`
- **Naming:** `YYYY-MM-DD-<topic>-test-plan.md` where `<topic>` is a slugified summary (lowercase, hyphens, no spaces)
- **Create directory if needed:** `mkdir -p docs/testing/plans`

---

## Plan Structure

Every test plan MUST follow this structure; omit optional sections, unused Setup labels and `**Blocked-by:**` lines where they do not apply. Replace the illustrative Base URL, expected results and citation tags with grounded project-specific details:

~~~markdown
# Test Plan: <title>

## Setup

**Base URL:** `http://127.0.0.1:8765`

**Required environment variables:**
- `QA_API_TOKEN` — bearer token for the users API
- `QA_USER_EMAIL` — login email of the test account
- `QA_USER_PASSWORD` — login password of the test account

**Required services:**
- App at `http://127.0.0.1:8765` (must be running before `/qa:run`; the plugin never starts it)

**Required databases:**
- `PGHOST` — host of the test PostgreSQL database (libpq reads it from the environment)
- `PGUSER` — test database user
- `PGDATABASE` — test database name
- `PGPASSWORD` — password read by libpq from the environment

## Source
- Type: <PR #N / branch <name> / last N commits / staged changes>
- Base: <main/master>
- Date: <YYYY-MM-DD>

## Changes Summary

<Brief description of what changed and what needs testing. List affected areas.>

## Blockers / Findings

Defects in the code under test that obstruct how it must be tested. Mandatory — write `None found.` when there are none.

### BLK-01: <one-line defect> — `(file:line)`
- **Impact on testing:** <affected scenarios and the spurious result the defect forces>
- **Remediation (human Setup prerequisite):** <human action before the run>
- **Blocks:** <scenario IDs carrying `**Blocked-by:** BLK-01`>

## Detected Tools
- Playwright MCP: <available/unavailable>
- HTTP client: <curl/httpie/unavailable>
- Database access: <psql/sqlite3/mysql/unavailable>

## FE Test Scenarios

### FE-01: <scenario name>
**Blocked-by:** BLK-01
- **Area:** <component/page>
- **Preconditions:** <what must be true before test>
- **Steps:**
  1. Open the login page in the browser
  2. Fill Email with `$QA_USER_EMAIL` and Password with `$QA_USER_PASSWORD`
  3. Click "Sign In"
- **Expected:** <welcome text shown> (path:line)
- **Edge cases:**
  - Password `nope`: <validation message shown> (path:line)
  - Missing email: <validation message shown> (path:line)

## BE Test Scenarios

### BE-01: <scenario name>
**Blocked-by:** BLK-01
- **Area:** <endpoint/service>
- **Method:** <HTTP method> <path>
- **Headers:** Authorization: Bearer $QA_API_TOKEN
- **Payload:** `<JSON body>`
- **Expected:** <status code>, <response body description> (path:line)
- **DB Check:** `SELECT COUNT(*) FROM resources WHERE status = 'active'` via the declared `PGHOST` / `PGUSER` / `PGDATABASE` / `PGPASSWORD` connection — <expected count>
- **Edge cases:**
  - <edge case with expected status and response> (path:line)
  - <second edge case with expected status and response> (path:line)

## Out of harness scope
- <check the browser/HTTP/DB harness cannot observe> — <one-clause harness reason>
~~~

---

## Scenario Naming

- FE scenarios: `FE-01`, `FE-02`, ... `FE-NN` (zero-padded two digits)
- BE scenarios: `BE-01`, `BE-02`, ... `BE-NN` (zero-padded two digits)
- Numbering is sequential within each section, starting from 01

---

## Grounding tags & assertion style

Every `**Expected:**` assertion and each edge-case expectation needs its own evidence tag:

- `(path:line)` cites a producing source line the author actually read in this working tree. Put one citation on the most load-bearing line per assertion; cite each edge case separately. Never invent a citation or cite a test for behavior it does not assert.
- `(unverified — confirm at run time)` marks an assertion whose producer cannot be read (for example, a foreign PR or pasted diff). If the source is on disk, read it instead; an unverified tag on a readable assertion is a defect. A mismatch still fails at run time.
- `(exact text — brittle)` marks quoted human-readable text that must be matched as a substring, not for equality. Use only when status and body shape cannot disambiguate the behavior; include a source citation or unverified tag as well.

Prefer stable status codes and response structure (keys/types) over exact message text. For a function-derived value (hash, slug, formatted filename), assert its generating rule and cite the producer rather than guessing an exact result; an exact literal needs a fixture or test that pins it. Replace the template's `(path:line)` with an actual cited path and line in each plan.
The BE tester sees only the output of `qa-redact`: it replaces values under sensitive name segments (`key`, `url`, `session`, etc.) and values equal to declared environment variables with `***`. For those fields, assert an observable property such as key presence or the type of an unaffected surrounding field/object, not the hidden value or its original type. For example, assert that `flags[0]` has a `key` field, not that `flags[0].key` equals `feature_x`; an assertion on masked data cannot establish PASS or FAIL and is reported as `SKIP — cannot confirm: value masked by qa-redact (<key>)` at run time. Keep other independently observable assertions, such as HTTP status codes, testable.

## Setup rules

- `## Setup` goes directly after `# Test Plan:`, before `## Source`. Omit the entire section when no prerequisites are needed; omit unused labels independently.
- At run time, resolve the base URL from `**Base URL:**` here, then the first URL in `## Source` or a scenario heading/bullet, then `QA_BASE_URL`. Project config is read only when authoring the plan, never at run time; if no URL resolves, testing aborts. The resolved host must be loopback (`localhost`, `127.0.0.1`, `::1`, `*.localhost`) unless the user passes `--allow-host` on the command line, and testers send requests and open pages only on that host: every scenario URL is a path under the Base URL or an absolute URL on its host, never a third-party host.
- Consumers read only the first backticked token of the `**Base URL:**` line or each `- ` bullet; following text is for humans. A `**Required environment variables:**` name must match `^QA_[A-Z0-9_]+$`. A `**Required databases:**` bullet must be such a `QA_` name, one of `PGHOST`, `PGUSER`, `PGDATABASE`, `PGPASSWORD`, `SQLITE_DB`, `MYSQL_HOST`, `MYSQL_USER`, `MYSQL_DATABASE`, `MYSQL_PWD` (Postgres/MySQL connection parts or a SQLite path; never a Postgres DSN), or a declared `mcp__` server name bound to the test database. Any other bullet is ignored with a warning, and testers never read a variable outside this set: the namespace keeps a plan from naming an unrelated secret of the launching shell. The plan generator never emits an `mcp__` bullet: only a human who knows the server's database may declare one.
- A credential in a header, cookie or login step (bearer token, API key, email/password) must be a `$QA_…` reference with that name declared under `**Required environment variables:**` — never the project's own variable name (`API_KEY`), a literal, or a placeholder such as `TOKEN`. A plan whose scenarios require auth but declare no name is defective. A deliberately invalid input in a negative edge case is not a credential.
- Minimize prerequisites: every declared environment variable name must be referenced by a scenario; omit labels that are unused. Starting the app or dependency is a human prerequisite under `**Required services:**`, never a scenario step.
- Run a DB check only through a connection declared under `**Required databases:**`: declare **all four** `PGHOST`, `PGUSER`, `PGDATABASE`, `PGPASSWORD` and run `psql -tAc '<SQL>'` (libpq reads the connection from the environment); `sqlite3 "$SQLITE_DB" '<SQL>'`; or `mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" "$MYSQL_DATABASE" -N -e '<SQL>'` with `MYSQL_PWD` read by the client from the environment. Declare all four MySQL names; never put passwords, DSNs, or request credentials in process argv. A declared `mcp__` server may be used instead; an available but undeclared MCP server must not be used. If no DB connection is declared, the API test still runs and its DB check is `SKIP`.
- Write DB checks against only the columns asserted; never put `SELECT *` in a plan. For CLI row checks, project just the needed columns to JSON and pipe the result through the installed `qa-redact.pl` before inspection or reporting. An MCP tool returns its results to the tester unsanitised: with a declared `mcp__` server request only a narrow non-sensitive aggregate; a row check requires a declared CLI connection, otherwise its DB check is `SKIP`.

## Harness scope

Scenario steps must be browser actions (FE) or HTTP requests / DB queries (BE) against an already-running app. Do not include `docker`, `make`, `npm`, migrations, file edits or infrastructure inspection as scenario steps. Record bring-up as a human `**Required services:**` prerequisite; list unobservable checks under optional `## Out of harness scope` as bullets with a one-clause harness reason (never scenario headings). A code defect that prevents a contract-correct result is a Blocker, not an out-of-scope check. If a tester nevertheless encounters an out-of-scope step, the scenario is `SKIP` with reason `out of harness scope: <step>`.

---

## Edge Case Generation Rules

For EVERY scenario, consider and include relevant edge cases from:

### Input boundaries
- Empty/null/missing values
- Maximum length strings
- Special characters (unicode, HTML entities, SQL metacharacters)
- Negative numbers, zero, boundary values (MAX_INT)

### Authentication & Authorization
- Unauthenticated request (no token)
- Expired token
- Valid token but insufficient permissions
- Another user's resource (IDOR)

### State
- Resource does not exist (404)
- Duplicate creation attempt (409)
- Concurrent modifications (race conditions)
- Resource in unexpected state (e.g., already deleted, already processed)

### Data integrity
- Required fields missing (422)
- Invalid data types (string where number expected)
- Referential integrity (foreign key does not exist)

### FE-specific
- Slow/no network connection
- Empty state (no data to display)
- Very long content (overflow, truncation)
- User not logged in
- Browser back/forward during operation

---

## Section Omission Rules

- If changes are **FE-only**: omit the `## BE Test Scenarios` section entirely
- If changes are **BE-only**: omit the `## FE Test Scenarios` section entirely
- If a tool is **unavailable**: note it in `## Detected Tools`; the tester reports `NEED_INFO kind=tool` at run time rather than labeling scenarios as skipped
- `## Setup` and `## Out of harness scope` are optional; `## Blockers / Findings` is mandatory for generated plans (`None found.` if none). Consumers ignore its absence in a hand-written plan

---

## Plan Quality Checklist

Before saving the plan, verify:

- [ ] Every scenario has at least 2 edge cases
- [ ] Every BE scenario has an expected status code
- [ ] Every FE scenario has concrete steps (not "test the form")
- [ ] DB Checks use actual table/column names from the codebase
- [ ] API paths match actual routes from the codebase
- [ ] No placeholder text (TBD, TODO, fill in later)
- [ ] `## Setup` is present whenever scenarios require a URL, credential or DB connection
- [ ] Every credential uses a `$QA_…` name declared under Setup, never a literal, a non-`QA_` name or a placeholder such as `TOKEN`
- [ ] Every scenario URL is a path under the Base URL or an absolute URL on its host
- [ ] Every `**Expected:**` and edge-case expectation has a grounded `(path:line)` or `(unverified — confirm at run time)` tag
- [ ] BE expectations and edge cases do not rely on a value or original type replaced with `***` by `qa-redact` (including declared environment variable values); assert observable presence or surrounding type instead
- [ ] `## Blockers / Findings` is present (or reads `None found.`)
- [ ] No scenario step is outside the browser / HTTP / DB harness scope
- [ ] For ≥2 independent boolean inputs, a `state-combination-planning` 2^N table appears above affected scenarios with a disposition for every row

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.