Writing Test Cases
Every test case lives in Kiwi and uses the same fields, whether a person or an AI tool wrote it.
2.1 The template
| Field | What to write |
|---|---|
| Summary | {QA ID} — {what happens}, e.g. API-OCRETAX-001 — Submit valid PDF returns 202 with tracking_id |
| Priority | P0 critical · P1 high · P2 medium · P3 low |
| Category | Functional · Negative · Boundary · Security · Performance · Eval |
| Component | The part of the system tested |
| Status | CONFIRMED when reviewed and ready · NEEDS_UPDATE while something is unclear |
| Text | Preconditions, steps, expected result, test data (format below) |
**Preconditions:**
- {environment, account, data needed}
**Steps:**
1. {one action}
2. {one action}
**Expected result:**
- {something you can see and check}
**Test data:** {inputs}2.2 QA ID
{AREA}-{FEATURE}-{NNN}, where AREA is API, WEB, DATA, PERF, or AI.
Examples: API-OCRETAX-001, WEB-KIWILOGIN-003, DATA-ETAXREPORT-004, PERF-OCRUPLOAD-001, AI-INVOICEEXTRACT-002.
Kiwi gives each case its own number (TC-1, TC-2…). Our QA ID goes at the
start of the Summary, and in the automated test’s name, so all three match up.
2.3 Priority
- P0: if this fails, the core flow is broken or data is wrong. Always in every release run.
- P1: a major feature is broken. Always in every release run.
- P2: broken, but there’s a workaround.
- P3: cosmetic or rare.
2.4 What to cover for each requirement
- Happy path: the normal flow works.
- Negative: wrong or missing input is rejected with a clear message.
- Boundary: empty, minimum, maximum, just over the maximum.
- Access: a user without permission can’t do it (if the feature has login or roles).
A requirement with only a happy-path case isn’t tested yet.
2.5 Example
**Summary:** API-OCRETAX-001 — Submit valid PDF returns 202 with tracking_id
**Priority:** P0 · **Category:** Functional · **Component:** HttpTrigger
**Preconditions:**
- OCR function app running locally (`func start`), Azurite, MongoDB, mock OCR
**Steps:**
1. POST a PDF to `/api/HttpTriggerOcrEtaxAsync` with `service=test-service`
**Expected result:**
- HTTP 202
- Response has a non-empty `tracking_id`
- `status` is `Uploaded`
- `message` is `File uploaded and queued for OCR processing.`
**Test data:** `C:\temp\invoice.pdf`Good test cases share three things: one behavior per case, one action per step, and expected results you can check (“HTTP 202”, not “works correctly”).
2.6 Manual run sheet (Excel)
Every test case is run manually first, and recorded in an Excel run sheet before anyone writes an automated test. The expected result is filled in before the test is run, never after seeing what the system did.
One sheet per Test Run, with these columns:
| Column | Filled in | Example |
|---|---|---|
| Test case ID | Before | API-OCRETAX-001 |
| Summary | Before | Submit valid PDF returns 202 with tracking_id |
| Priority | Before | P0 |
| Preconditions | Before | func start running, C:\temp\invoice.pdf exists |
| Steps | Before | POST the PDF with service=test-service |
| Expected result | Before | 202; tracking_id not empty; status = Uploaded |
| Actual result | After | 202; tracking_id = a1b2…; status = Uploaded |
| Status | After | PASSED / FAILED / BLOCKED / WAIVED |
| Evidence | After | evidence\API-OCRETAX-001.txt |
| Tester | After | your name |
| Date / Build | After | 2026-09-22 / 1.2-rc1 |
| Bug | After | link, if FAILED |
Rules:
- Fill the “before” columns first. Writing the expected result after the run turns a test into a description of whatever happened.
- Write actual results as facts, not judgements: “HTTP 500, body empty”, not “didn’t work”.
- Save evidence next to the sheet: response text, screenshot, or query output.
- Attach the finished sheet to the Test Run in Kiwi, and set each execution’s status there too (section 8.2).
Automate a case once it passes manually and you know it will be repeated every release. Manual testing stays for exploratory work and anything needing human judgement.
2.7 When something isn’t specified
If the requirement doesn’t say what should happen (an error message, a size
limit), write ASSUMPTION: in the expected result, set the status to
NEEDS_UPDATE, and ask the developer or product owner. Don’t guess.
This applies to AI-generated test cases too: the AI must mark assumptions
instead of inventing behavior, and a person reviews every case before it’s
CONFIRMED.