Testing Strategy

March 27, 2026 · View on GitHub

简体中文 | English

Testing Strategy

Writing tests is one of the highest-ROI uses of AI coding tools. AI excels at generating repetitive test code, but you need to tell it what to test and how — otherwise it produces a pile of useless tests.


Core Principles

1. Define a Testing Strategy Before Asking AI to Write Tests

❌ Bad: "Write tests for this file"
✅ Good: "Write unit tests for src/services/order.ts.
    Focus on:
    - createOrder: should throw when inventory is insufficient
    - calculateTotal: edge cases around stacked discounts
    - cancelOrder: should reject cancellation of shipped orders
    Use Vitest. Mock the database layer."

2. Spell Out the Edge Cases

AI is great at happy-path tests but tends to miss boundary conditions. Be explicit:

Write tests for the parseDate function covering:
- Normal input: "2024-01-15"
- Empty string
- null / undefined
- Invalid format: "not-a-date"
- Leap year: "2024-02-29"
- Timezone boundary: around UTC midnight

3. Don't Let AI Test Implementation Details

❌ Bad: Test that getUserList calls db.query exactly N times
✅ Good: Test that getUserList returns the correct user list,
    pagination parameters work, and an empty result returns an empty array

Test behavior, not implementation. Otherwise every refactor breaks your tests.


Best Practices by Test Type

Unit Tests

Write unit tests for all exported functions in src/utils/validator.ts.

Requirements:
1. At least 3 test cases per function (normal, boundary, error)
2. Group with describe blocks by function
3. Use descriptive test names, e.g., "should return false for empty string"
4. Don't mock dependencies of pure functions

Integration Tests

Write integration tests for the order creation flow.
Scope: Controller → Service → Repository
Use a test database (don't mock the DB). Clear data before each test.

Scenarios:
1. Create order successfully → inventory decreases → returns order ID
2. Insufficient inventory → returns 400 → inventory unchanged
3. Concurrent creation → must not oversell

API / E2E Tests

Write API tests for POST /api/auth/login.

Test cases:
1. Correct credentials → 200 + token
2. Wrong password → 401
3. Non-existent account → 401 (don't reveal "account not found")
4. Missing fields → 400 + specify which field is missing
5. 5 consecutive failures → 429 rate limit

Efficient Workflows for AI-Generated Tests

Workflow 1: Code First, Tests After

1. Implement the feature
2. Have AI read the code and generate tests
3. Run the tests — check for failures
4. Failing tests = potential bugs. Have AI determine if it's a code issue or a test issue

Workflow 2: TDD — Tests First, Code After

1. Describe the requirements. Have AI write test cases only (no implementation)
2. Review the test cases to confirm they cover the desired behavior
3. Have AI write the implementation. Run tests until they all pass
4. Refactor with confidence (tests have your back)

TDD works especially well with AI because the tests serve as an unambiguous definition of done.

Workflow 3: Tests as a Safety Net

Need to refactor src/services/payment.ts.
First, write comprehensive tests covering existing behavior.
Once they pass, start refactoring.
Run tests after every change.

Common Anti-Patterns

Anti-PatternProblemDo This Instead
Have AI generate all tests at onceHigh quantity, low quality — many meaningless assertionsGenerate per module, review each batch
Only test the happy pathEdge cases and error paths are uncoveredExplicitly list boundary conditions
Mock everythingTests diverge from real behaviorOnly mock external dependencies (network, DB). Don't mock pure logic
Copy-paste testsMassive duplication, high maintenance costUse test.each or parameterized tests
Commit tests without reviewingAI may assert incorrect expected valuesVerify every assertion is correct
Chase 100% coverageWastes time testing getters/settersFocus coverage on core logic, not vanity metrics

Tool-Specific Testing Tips

ToolTip
Claude CodeJust say "run the tests and check the results" — it will execute and fix failing cases automatically
CursorSelect a function → Cmd+K → "write unit tests" for quick generation
CopilotOpen a test file, type describe('...'), and autocomplete generates test cases
AiderSet auto-test: true to run tests automatically after every code change
KiroDefine test cases in the Spec; they're verified automatically during implementation