Project 22: Test Generator Hook (Auto-Test on Write)

Project 22: Test Generator Hook (Auto-Test on Write)

Build a PostToolUse hook that automatically generates or updates tests when Kiro writes production code - maintaining test coverage without manual effort.


Learning Objectives

By completing this project, you will:

  1. Master Kiroโ€™s hook system including PostToolUse triggers, matchers, and exit codes
  2. Understand test file mapping conventions and how to maintain test/source correspondence
  3. Implement recursive AI invocation by calling Kiro from within a hook
  4. Design test generation strategies that produce meaningful, runnable tests
  5. Build quality gates that validate generated tests before accepting them

Deep Theoretical Foundation

The Testing Paradox

Developers know tests are important, yet test coverage often lags behind production code. This creates a paradox:

The Testing Debt Cycle:

         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚                                                       โ”‚
         โ–ผ                                                       โ”‚
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                               โ”‚
  โ”‚ Write Code  โ”‚                                               โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                               โ”‚
         โ”‚                                                       โ”‚
         โ–ผ                                                       โ”‚
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    "I'll add tests later"    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
  โ”‚  Ship Fast  โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ Test Debt   โ”‚โ”€โ”˜
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                              โ”‚ Accumulates โ”‚
                                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                                      โ”‚
                                                      โ–ผ
                                               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                               โ”‚ Refactoring โ”‚
                                               โ”‚ Becomes     โ”‚
                                               โ”‚ Terrifying  โ”‚
                                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Solution: Automatic test generation as code is written - not later.

Kiro Hooks Architecture

Hooks are the extension points that let you customize Kiroโ€™s behavior. Think of them as middleware for AI operations:

Hook Execution Flow:

User Request: "Create a user service"
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        Kiro Agent                                โ”‚
โ”‚                            โ”‚                                     โ”‚
โ”‚                            โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚ preToolUse Hook (optional)                                  โ”‚โ”‚
โ”‚  โ”‚ โ€ข Inspect planned operation                                 โ”‚โ”‚
โ”‚  โ”‚ โ€ข Can BLOCK with exit code 2                               โ”‚โ”‚
โ”‚  โ”‚ โ€ข Can MODIFY with JSON output                              โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ”‚                            โ”‚                                     โ”‚
โ”‚                            โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚ Tool Execution                                              โ”‚โ”‚
โ”‚  โ”‚ โ€ข write: Create/modify files                                โ”‚โ”‚
โ”‚  โ”‚ โ€ข bash: Run commands                                        โ”‚โ”‚
โ”‚  โ”‚ โ€ข etc.                                                      โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ”‚                            โ”‚                                     โ”‚
โ”‚                            โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚ postToolUse Hook โ—„โ”€โ”€โ”€โ”€ YOUR HOOK RUNS HERE                 โ”‚โ”‚
โ”‚  โ”‚ โ€ข Observe completed operation                               โ”‚โ”‚
โ”‚  โ”‚ โ€ข Trigger side effects (test generation!)                  โ”‚โ”‚
โ”‚  โ”‚ โ€ข Can provide feedback to agent                            โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ”‚                            โ”‚                                     โ”‚
โ”‚                            โ–ผ                                     โ”‚
โ”‚                     Continue to next tool                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Hook Input/Output Protocol

Hooks communicate with Kiro through stdin/stdout and exit codes:

Hook Input (JSON via stdin):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ {                                                                โ”‚
โ”‚   "hook_event_name": "postToolUse",                             โ”‚
โ”‚   "cwd": "/path/to/project",                                    โ”‚
โ”‚   "tool_name": "write",                                         โ”‚
โ”‚   "tool_input": {                                               โ”‚
โ”‚     "file_path": "src/services/user.ts",                        โ”‚
โ”‚     "content": "export class UserService { ... }"               โ”‚
โ”‚   },                                                            โ”‚
โ”‚   "tool_response": {                                            โ”‚
โ”‚     "success": true,                                            โ”‚
โ”‚     "result": "File written successfully"                       โ”‚
โ”‚   }                                                              โ”‚
โ”‚ }                                                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Hook Output (exit codes):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Exit Code  โ”‚ Meaning                                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚     0      โ”‚ Success - continue normally                        โ”‚
โ”‚     1      โ”‚ Error - but continue (logged as warning)          โ”‚
โ”‚     2      โ”‚ BLOCK - stop and send stdout to agent as feedback โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Test File Mapping Conventions

Different projects use different conventions for mapping source files to test files:

Common Mapping Patterns:

Pattern 1: __tests__ directories (Jest default)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ src/                                    โ”‚
โ”‚ โ”œโ”€โ”€ services/                           โ”‚
โ”‚ โ”‚   โ”œโ”€โ”€ user.ts           โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚ โ”‚   โ””โ”€โ”€ __tests__/                  โ”‚   โ”‚
โ”‚ โ”‚       โ””โ”€โ”€ user.test.ts  โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚ โ””โ”€โ”€ utils/                              โ”‚
โ”‚     โ”œโ”€โ”€ format.ts         โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚     โ””โ”€โ”€ __tests__/                  โ”‚   โ”‚
โ”‚         โ””โ”€โ”€ format.test.tsโ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Pattern 2: Parallel test directory
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ src/                                    โ”‚
โ”‚ โ””โ”€โ”€ services/                           โ”‚
โ”‚     โ””โ”€โ”€ user.ts           โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚                                         โ”‚โ”‚
โ”‚ test/                                   โ”‚โ”‚
โ”‚ โ””โ”€โ”€ services/                           โ”‚โ”‚
โ”‚     โ””โ”€โ”€ user.test.ts      โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Pattern 3: Co-located tests
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ src/                                    โ”‚
โ”‚ โ””โ”€โ”€ services/                           โ”‚
โ”‚     โ”œโ”€โ”€ user.ts           โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚     โ””โ”€โ”€ user.test.ts      โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Test Generation Strategies

AI can generate tests in multiple ways, each with trade-offs:

Strategy Comparison:

1. EXAMPLE-BASED TESTING
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ describe('add', () => {                                     โ”‚
   โ”‚   it('adds 2 + 2 to equal 4', () => {                      โ”‚
   โ”‚     expect(add(2, 2)).toBe(4);                             โ”‚
   โ”‚   });                                                       โ”‚
   โ”‚ });                                                         โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
   Pros: Simple, readable, fast
   Cons: Limited coverage, may miss edge cases

2. BOUNDARY-BASED TESTING
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ describe('divide', () => {                                  โ”‚
   โ”‚   it('handles division by zero', () => {                   โ”‚
   โ”‚     expect(() => divide(1, 0)).toThrow();                  โ”‚
   โ”‚   });                                                       โ”‚
   โ”‚   it('handles MAX_SAFE_INTEGER', () => {                   โ”‚
   โ”‚     expect(divide(Number.MAX_SAFE_INTEGER, 1)).toBe(...);  โ”‚
   โ”‚   });                                                       โ”‚
   โ”‚ });                                                         โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
   Pros: Catches edge cases, tests limits
   Cons: Requires understanding of domain

3. BEHAVIOR-BASED TESTING
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ describe('UserService', () => {                             โ”‚
   โ”‚   it('creates user with valid data', async () => {         โ”‚
   โ”‚     const user = await service.create({ name: 'Alice' });  โ”‚
   โ”‚     expect(user.id).toBeDefined();                         โ”‚
   โ”‚   });                                                       โ”‚
   โ”‚   it('rejects duplicate email', async () => {              โ”‚
   โ”‚     await service.create({ email: 'a@b.com' });            โ”‚
   โ”‚     await expect(service.create({ email: 'a@b.com' }))     โ”‚
   โ”‚       .rejects.toThrow('Email already exists');            โ”‚
   โ”‚   });                                                       โ”‚
   โ”‚ });                                                         โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
   Pros: Tests real behavior, maintainable
   Cons: Requires understanding of requirements

Recursive AI Invocation Pattern

Your hook can call Kiro itself to generate tests. This creates a powerful meta-pattern:

Recursive AI Invocation:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Kiro Agent (Main Session)                                        โ”‚
โ”‚                                                                  โ”‚
โ”‚    User: "Create a UserService class"                           โ”‚
โ”‚         โ”‚                                                        โ”‚
โ”‚         โ–ผ                                                        โ”‚
โ”‚    [Writes src/services/user.ts]                                โ”‚
โ”‚         โ”‚                                                        โ”‚
โ”‚         โ–ผ                                                        โ”‚
โ”‚    postToolUse Hook Fires                                        โ”‚
โ”‚         โ”‚                                                        โ”‚
โ”‚         โ–ผ                                                        โ”‚
โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚    โ”‚ Test Generator Hook (subprocess)                        โ”‚   โ”‚
โ”‚    โ”‚                                                         โ”‚   โ”‚
โ”‚    โ”‚   1. Read the new file content                         โ”‚   โ”‚
โ”‚    โ”‚   2. Spawn Kiro in non-interactive mode               โ”‚   โ”‚
โ”‚    โ”‚   3. Prompt: "Write tests for this code: ..."         โ”‚   โ”‚
โ”‚    โ”‚                                                         โ”‚   โ”‚
โ”‚    โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚    โ”‚   โ”‚ Kiro (Subprocess)                               โ”‚  โ”‚   โ”‚
โ”‚    โ”‚   โ”‚ โ€ข Analyzes code                                 โ”‚  โ”‚   โ”‚
โ”‚    โ”‚   โ”‚ โ€ข Generates test file                          โ”‚  โ”‚   โ”‚
โ”‚    โ”‚   โ”‚ โ€ข Returns test content                         โ”‚  โ”‚   โ”‚
โ”‚    โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚    โ”‚                                                         โ”‚   โ”‚
โ”‚    โ”‚   4. Validate generated tests                          โ”‚   โ”‚
โ”‚    โ”‚   5. Write test file                                   โ”‚   โ”‚
โ”‚    โ”‚   6. Run tests to verify                               โ”‚   โ”‚
โ”‚    โ”‚                                                         โ”‚   โ”‚
โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                  โ”‚
โ”‚    [Continues with main conversation]                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Testing Pyramid and AI Generation

Understanding where AI-generated tests fit in the testing pyramid:

Testing Pyramid with AI Generation:

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                   โ•ฑ  E2E Tests   โ•ฒ           AI can help
                  โ•ฑ   (Few, Slow)  โ•ฒ          but human
                 โ•ฑโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฒ          review needed
                โ•ฑ                   โ•ฒ
               โ•ฑ   Integration       โ•ฒ        AI good at
              โ•ฑ      Tests            โ•ฒ       mocking setup
             โ•ฑ     (Some, Medium)      โ•ฒ
            โ•ฑโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฒ
           โ•ฑ                             โ•ฒ
          โ•ฑ        Unit Tests             โ•ฒ   AI EXCELS
         โ•ฑ       (Many, Fast)              โ•ฒ  HERE
        โ•ฑโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฒ

AI-Generated Test Distribution:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Unit Tests (90%)          Integration (9%)        E2E (1%)    โ”‚
โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  โ–ˆโ–ˆ                      โ–ˆ           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Real-World Analogy: The Automated Proofreader

Think of this hook like having a proofreader who automatically writes a quiz for every chapter you write:

  1. Author writes Chapter 5 (Developer writes UserService)
  2. Proofreader reads it (Hook detects file write)
  3. Creates comprehension questions (Generates test cases)
  4. Runs questions on chapter (Executes tests)
  5. Verifies answers are correct (Tests pass)

If the quiz fails, either:

  • The chapter has errors (bugs in code)
  • The quiz is wrong (bad test generation)

Historical Context

Test generation has evolved significantly:

Evolution of Test Generation:

1970s: Manual Testing Only
       โ””โ”€โ–บ Testers write all test cases by hand

1990s: Code Coverage Tools
       โ””โ”€โ–บ Tools show WHAT to test, humans write HOW

2000s: Test Frameworks (JUnit, pytest)
       โ””โ”€โ–บ Structured test organization, still manual

2010s: Property-Based Testing
       โ””โ”€โ–บ Tools generate inputs, humans define properties

2020s: AI-Powered Generation โ—„โ”€โ”€โ”€ YOU ARE HERE
       โ””โ”€โ–บ AI understands code, generates meaningful tests

Book References

For deeper understanding:

  • โ€œTest Driven Developmentโ€ by Kent Beck - The foundational TDD text
  • โ€œGrowing Object-Oriented Software, Guided by Testsโ€ by Freeman & Pryce - Testing as design
  • โ€œWorking Effectively with Legacy Codeโ€ by Michael Feathers - Testing untested code
  • โ€œxUnit Test Patternsโ€ by Gerard Meszaros - Test organization and patterns

Complete Project Specification

What You Are Building

A PostToolUse hook that:

  1. Triggers on file writes to production code (src/*)
  2. Maps source to test files using project conventions
  3. Invokes Kiro to generate appropriate tests
  4. Validates generated tests by running them
  5. Reports results back to the main Kiro session

Functional Requirements

Feature Behavior
Detection Trigger on any write to src/**/*.ts or src/**/*.js
Mapping Find or create corresponding test file
Generation Generate tests covering all exported functions
Validation Run generated tests with Vitest/Jest
Reporting Output summary of generated tests

Non-Functional Requirements

  • Latency: Complete test generation within 30 seconds
  • Reliability: Handle failures gracefully, never block main workflow
  • Quality: Generated tests should pass and provide real coverage
  • Configurability: Support different test conventions and frameworks

Solution Architecture

High-Level Component Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                           Kiro CLI                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚                    Main Agent Session                            โ”‚โ”‚
โ”‚  โ”‚                                                                  โ”‚โ”‚
โ”‚  โ”‚  User: "Create a UserService with CRUD operations"              โ”‚โ”‚
โ”‚  โ”‚         โ”‚                                                        โ”‚โ”‚
โ”‚  โ”‚         โ–ผ                                                        โ”‚โ”‚
โ”‚  โ”‚  [write tool: src/services/user.ts]                             โ”‚โ”‚
โ”‚  โ”‚         โ”‚                                                        โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
             โ”‚
             โ”‚ postToolUse event
             โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Test Generator Hook                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ File Analyzer โ”‚  โ”‚ Test Mapper   โ”‚  โ”‚ Kiro Invoker  โ”‚           โ”‚
โ”‚  โ”‚ โ€ข Parse TS/JS โ”‚  โ”‚ โ€ข Find test   โ”‚  โ”‚ โ€ข Spawn Kiro  โ”‚           โ”‚
โ”‚  โ”‚ โ€ข Extract     โ”‚  โ”‚   file path   โ”‚  โ”‚ โ€ข Send prompt โ”‚           โ”‚
โ”‚  โ”‚   exports     โ”‚  โ”‚ โ€ข Apply       โ”‚  โ”‚ โ€ข Get tests   โ”‚           โ”‚
โ”‚  โ”‚               โ”‚  โ”‚   conventions โ”‚  โ”‚               โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚          โ”‚                  โ”‚                  โ”‚                    โ”‚
โ”‚          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ”‚
โ”‚                             โ–ผ                                        โ”‚
โ”‚                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                           โ”‚
โ”‚                   โ”‚   Test Validator    โ”‚                           โ”‚
โ”‚                   โ”‚  โ€ข Write test file  โ”‚                           โ”‚
โ”‚                   โ”‚  โ€ข Run vitest/jest  โ”‚                           โ”‚
โ”‚                   โ”‚  โ€ข Parse results    โ”‚                           โ”‚
โ”‚                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                           โ”‚
โ”‚                             โ”‚                                        โ”‚
โ”‚                             โ–ผ                                        โ”‚
โ”‚                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                           โ”‚
โ”‚                   โ”‚   Result Reporter   โ”‚                           โ”‚
โ”‚                   โ”‚  โ€ข Format output    โ”‚                           โ”‚
โ”‚                   โ”‚  โ€ข Exit code        โ”‚                           โ”‚
โ”‚                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Data Flow: End-to-End

1. File Write Detected
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ stdin: {                                                        โ”‚
   โ”‚   "tool_name": "write",                                         โ”‚
   โ”‚   "tool_input": {                                               โ”‚
   โ”‚     "file_path": "src/services/user.ts",                        โ”‚
   โ”‚     "content": "export class UserService {\n  create()..."      โ”‚
   โ”‚   }                                                              โ”‚
   โ”‚ }                                                                โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
2. Analyze Source File
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ Exports found:                                                  โ”‚
   โ”‚ โ€ข class UserService                                             โ”‚
   โ”‚   - method: create(data: CreateUserDto): Promise<User>         โ”‚
   โ”‚   - method: findById(id: string): Promise<User | null>         โ”‚
   โ”‚   - method: update(id: string, data: UpdateDto): Promise<User> โ”‚
   โ”‚   - method: delete(id: string): Promise<void>                  โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
3. Map to Test File
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ Source: src/services/user.ts                                    โ”‚
   โ”‚ Test:   src/services/__tests__/user.test.ts                    โ”‚
   โ”‚ Status: Does not exist (CREATE)                                 โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
4. Generate Tests via Kiro
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ kiro-cli --print "                                              โ”‚
   โ”‚   Write comprehensive tests for this TypeScript class.         โ”‚
   โ”‚   Use Vitest. Mock external dependencies.                      โ”‚
   โ”‚                                                                 โ”‚
   โ”‚   Source code:                                                  โ”‚
   โ”‚   \`\`\`typescript                                                โ”‚
   โ”‚   ${sourceCode}                                                 โ”‚
   โ”‚   \`\`\`                                                          โ”‚
   โ”‚                                                                 โ”‚
   โ”‚   Output ONLY the test file content, no explanations.          โ”‚
   โ”‚ "                                                               โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
5. Write and Validate Tests
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ $ vitest run src/services/__tests__/user.test.ts               โ”‚
   โ”‚                                                                 โ”‚
   โ”‚  โœ“ UserService                                                  โ”‚
   โ”‚    โœ“ create() should create user with valid data               โ”‚
   โ”‚    โœ“ create() should throw on invalid email                    โ”‚
   โ”‚    โœ“ findById() should return user                             โ”‚
   โ”‚    โœ“ findById() should return null for missing                 โ”‚
   โ”‚    โœ“ update() should update existing user                      โ”‚
   โ”‚    โœ“ delete() should remove user                               โ”‚
   โ”‚                                                                 โ”‚
   โ”‚  6 passed                                                       โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
6. Report to Main Session
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ stdout:                                                         โ”‚
   โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
   โ”‚ โ”‚ TEST GENERATION COMPLETE                                    โ”‚ โ”‚
   โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚
   โ”‚ โ”‚ Source: src/services/user.ts                                โ”‚ โ”‚
   โ”‚ โ”‚ Tests:  src/services/__tests__/user.test.ts                 โ”‚ โ”‚
   โ”‚ โ”‚                                                             โ”‚ โ”‚
   โ”‚ โ”‚ Generated: 6 test cases                                     โ”‚ โ”‚
   โ”‚ โ”‚ Passed:    6/6                                              โ”‚ โ”‚
   โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
   โ”‚                                                                 โ”‚
   โ”‚ exit code: 0                                                    โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key Interfaces

// Hook input structure (from Kiro)
interface HookInput {
  hook_event_name: 'postToolUse';
  cwd: string;
  tool_name: string;
  tool_input: {
    file_path: string;
    content: string;
  };
  tool_response: {
    success: boolean;
    result: string;
  };
}

// Parsed export information
interface ExportInfo {
  type: 'function' | 'class' | 'const';
  name: string;
  parameters?: Parameter[];
  returnType?: string;
  methods?: MethodInfo[];
}

// Test generation result
interface TestGenerationResult {
  sourcePath: string;
  testPath: string;
  testContent: string;
  testCount: number;
  passed: number;
  failed: number;
  errors: string[];
}

// Configuration
interface TestGeneratorConfig {
  testFramework: 'vitest' | 'jest';
  testPattern: '__tests__' | 'parallel' | 'colocated';
  testSuffix: '.test.ts' | '.spec.ts';
  excludePaths: string[];
}

Technology Choices

Component Technology Rationale
Hook Runtime Bun Fast startup, native TS
AST Parsing TypeScript Compiler API Accurate export detection
Test Framework Vitest Modern, fast, ESM-native
Kiro Invocation Child process Clean separation
Config JSON in .kiro/ Standard Kiro pattern

Phased Implementation Guide

Phase 1: Hook Foundation (Days 1-2)

Goal: Create a hook that triggers on file writes and logs events.

Tasks:

  1. Create the hook script file
  2. Configure hook in .kiro/settings.json
  3. Parse stdin JSON input
  4. Filter for relevant file writes (src/*/.ts)
  5. Log detection to a file for debugging

Hints:

  • Hooks receive JSON on stdin, not as arguments
  • Use a matcher pattern to only trigger on write tool
  • Start with exit code 0 to not block Kiro

Configuration (.kiro/settings.json):

{
  "hooks": {
    "postToolUse": [
      {
        "matcher": "write",
        "command": "bun run /path/to/hooks/test-generator.ts"
      }
    ]
  }
}

Starter Code:

#!/usr/bin/env bun

import { readFileSync } from 'fs';

// Read all of stdin
const input = readFileSync(0, 'utf-8');
const event = JSON.parse(input);

// Only process TypeScript/JavaScript files in src/
const filePath = event.tool_input?.file_path;
if (!filePath?.match(/^src\/.*\.(ts|js)$/)) {
  process.exit(0);
}

console.log(`Detected write to: ${filePath}`);
process.exit(0);

Phase 2: Test File Mapping (Days 3-4)

Goal: Determine the correct test file path for any source file.

Tasks:

  1. Detect project test convention (check for existing patterns)
  2. Implement path transformation logic
  3. Handle edge cases (index files, nested directories)
  4. Check if test file already exists

Hints:

  • Look for existing __tests__ directories or *.test.ts files
  • The package.json may have jest/vitest config with testMatch
  • Create missing directories as needed

Path Mapping Logic:

function mapSourceToTest(sourcePath: string, config: TestGeneratorConfig): string {
  // Example: src/services/user.ts
  const dir = path.dirname(sourcePath);  // src/services
  const base = path.basename(sourcePath, path.extname(sourcePath));  // user
  const ext = path.extname(sourcePath);  // .ts

  switch (config.testPattern) {
    case '__tests__':
      // src/services/__tests__/user.test.ts
      return path.join(dir, '__tests__', `${base}${config.testSuffix}`);

    case 'parallel':
      // test/services/user.test.ts
      return sourcePath
        .replace(/^src\//, 'test/')
        .replace(ext, config.testSuffix);

    case 'colocated':
      // src/services/user.test.ts
      return sourcePath.replace(ext, config.testSuffix);
  }
}

Phase 3: AI Test Generation (Days 5-8)

Goal: Invoke Kiro to generate test content.

Tasks:

  1. Extract exports from source file (using regex or TS compiler)
  2. Build a prompt describing what tests to generate
  3. Invoke Kiro CLI in non-interactive mode
  4. Parse and clean the generated test code
  5. Handle generation failures gracefully

Hints:

  • Use kiro-cli --print "prompt" for single-response mode
  • Include the full source code in the prompt for context
  • Ask for specific test patterns (describe/it/expect)
  • Request no markdown code fences in output

Prompt Template:

const prompt = `
Generate comprehensive unit tests for the following TypeScript code.

Requirements:
- Use Vitest (import { describe, it, expect } from 'vitest')
- Mock any external dependencies
- Test both success and error cases
- Include edge cases (null, undefined, empty arrays)
- Use descriptive test names

Source code to test:
\`\`\`typescript
${sourceCode}
\`\`\`

Output ONLY the complete test file. No explanations or markdown.
`;

Phase 4: Test Validation and Polish (Days 9-14)

Goal: Validate generated tests and provide quality feedback.

Tasks:

  1. Write generated tests to test file
  2. Run tests with Vitest/Jest
  3. Parse test runner output
  4. Handle test failures (regenerate or report)
  5. Format final output for Kiro session

Hints:

  • Use vitest run --reporter=json for parseable output
  • If tests fail, you might try regenerating once
  • Consider a โ€œdry runโ€ mode that shows but doesnโ€™t write tests
  • Cache successful generations to avoid redundant work

Test Execution:

async function validateTests(testPath: string): Promise<TestResult> {
  const result = await $`vitest run ${testPath} --reporter=json`.quiet();

  const report = JSON.parse(result.stdout);
  return {
    passed: report.numPassedTests,
    failed: report.numFailedTests,
    errors: report.testResults
      .flatMap(r => r.assertionResults)
      .filter(a => a.status === 'failed')
      .map(a => a.failureMessages)
  };
}

Testing Strategy

Unit Tests for the Hook

describe('TestGenerator Hook', () => {
  describe('shouldProcessFile', () => {
    it('returns true for src TypeScript files', () => {
      expect(shouldProcessFile('src/services/user.ts')).toBe(true);
    });

    it('returns false for test files', () => {
      expect(shouldProcessFile('src/services/user.test.ts')).toBe(false);
    });

    it('returns false for non-src files', () => {
      expect(shouldProcessFile('node_modules/pkg/index.ts')).toBe(false);
    });
  });

  describe('mapSourceToTest', () => {
    it('maps to __tests__ directory', () => {
      const config = { testPattern: '__tests__', testSuffix: '.test.ts' };
      expect(mapSourceToTest('src/services/user.ts', config))
        .toBe('src/services/__tests__/user.test.ts');
    });
  });
});

Integration Tests

describe('Test Generation Integration', () => {
  it('generates valid tests for a simple function', async () => {
    const source = `
      export function add(a: number, b: number): number {
        return a + b;
      }
    `;

    const testContent = await generateTests(source);

    // Write and run
    await writeFile('test-output.test.ts', testContent);
    const result = await $`vitest run test-output.test.ts`.quiet();

    expect(result.exitCode).toBe(0);
  });
});

End-to-End Validation

# 1. Start Kiro with the hook configured
kiro-cli chat

# 2. Ask Kiro to write some code
> "Create a simple StringUtils class with capitalize and reverse methods"

# 3. Observe hook output
# Should see test generation summary after file is written

# 4. Verify test file exists
ls src/**/__tests__/*.test.ts

# 5. Run tests manually
npm test

Common Pitfalls and Debugging

Pitfall 1: Hook Not Triggering

Symptom: No output when Kiro writes files

Debugging:

# Check hook is configured
cat .kiro/settings.json | jq '.hooks.postToolUse'

# Test hook manually
echo '{"hook_event_name":"postToolUse","tool_name":"write","tool_input":{"file_path":"src/test.ts","content":"test"}}' | bun run hooks/test-generator.ts

# Check for errors in hook script
bun check hooks/test-generator.ts

Pitfall 2: Kiro Subprocess Hangs

Symptom: Hook never completes, blocks main session

Cause: Kiro waiting for interactive input

Solution:

// Use --print for non-interactive mode
const result = await $`kiro-cli --print ${prompt}`.timeout(30000);

// Add timeout to prevent infinite hangs
// Consider --no-mcp to avoid nested MCP issues

Pitfall 3: Generated Tests Have Syntax Errors

Symptom: Tests fail to parse/compile

Debugging:

# Save raw AI output for inspection
echo "$generatedCode" > /tmp/raw-test-output.ts

# Check for common issues
# - Markdown code fences in output
# - Missing imports
# - Wrong test framework syntax

Prevention:

// Clean markdown artifacts
function cleanGeneratedCode(code: string): string {
  return code
    .replace(/```typescript\n?/g, '')
    .replace(/```\n?/g, '')
    .trim();
}

Pitfall 4: Tests Pass But Donโ€™t Test Anything

Symptom: 100% passing but no assertions

Cause: AI generated empty test bodies

Solution:

// Validate test quality
function hasRealAssertions(testCode: string): boolean {
  const expectCount = (testCode.match(/expect\(/g) || []).length;
  const testCount = (testCode.match(/it\(/g) || []).length;

  // At least one expect per test
  return expectCount >= testCount;
}

Extensions and Challenges

Extension 1: Incremental Test Updates

When code changes, update only affected tests instead of regenerating all:

interface TestDiff {
  added: string[];    // New functions to test
  modified: string[]; // Changed signatures
  removed: string[];  // Deleted functions
}

function computeTestDiff(oldCode: string, newCode: string): TestDiff { ... }

Extension 2: Test Coverage Targeting

Generate tests specifically for uncovered lines:

# Get coverage report
vitest run --coverage --reporter=json

# Parse uncovered lines
# Generate targeted tests for those lines

Extension 3: Mock Generation

Automatically generate mocks for dependencies:

// Detect imports
import { db } from '../database';
import { logger } from '../utils/logger';

// Generate mock file
// __mocks__/database.ts

Extension 4: Mutation Testing Integration

Use mutation testing to verify test quality:

# After generating tests
stryker run --mutate="src/services/user.ts"
# Report mutation score

Extension 5: Test Style Configuration

Support different testing styles per project:

{
  "testGenerator": {
    "style": "bdd",           // or "tdd", "given-when-then"
    "assertions": "chai",      // or "expect", "assert"
    "mocking": "vitest",       // or "jest", "sinon"
    "coverage": 80             // minimum target
  }
}

Real-World Connections

Industry Adoption

AI-assisted test generation is being adopted by:

  • GitHub Copilot: Suggests test completions in IDE
  • Tabnine: Generates test snippets from comments
  • Amazon CodeWhisperer: Creates test cases from function signatures
  • Diffblue Cover: Enterprise Java test generation

Production Patterns

Pattern Use Case
Pre-commit Generate tests before commit, block if tests fail
PR Validation Generate tests for changed files in CI
Coverage Gate Require minimum coverage for new code
Mutation Score Validate test quality, not just coverage

Limitations to Acknowledge

AI-generated tests are not perfect:

  1. May miss business logic edge cases - AI doesnโ€™t know your domain
  2. Can create brittle tests - Testing implementation, not behavior
  3. Might mock incorrectly - Dependencies need human review
  4. Coverage != Quality - High coverage with weak assertions

Self-Assessment Checklist

Knowledge Verification

  • Can you explain the difference between preToolUse and postToolUse hooks?
  • What exit codes can a hook return and what do they mean?
  • How does the hook receive information about the tool operation?
  • What is the recursive AI invocation pattern?
  • Why might you want to validate generated tests before accepting them?

Implementation Verification

  • Your hook triggers when Kiro writes TypeScript files
  • Test files are created in the correct location per project convention
  • Generated tests import the correct testing framework
  • Tests actually pass when run
  • The hook completes within the timeout period

Quality Verification

  • Generated tests have meaningful assertions (not just empty it() blocks)
  • Edge cases are tested (null, undefined, empty, boundaries)
  • Mocks are properly set up for external dependencies
  • Test names describe the expected behavior

Integration Verification

  • The hook works seamlessly during normal Kiro usage
  • Failures are reported helpfully, not silently swallowed
  • The hook can be disabled when not wanted
  • Configuration is flexible enough for different projects

Summary

Building a test generator hook teaches you:

  1. Hook System Mastery: How to extend Kiroโ€™s behavior at runtime
  2. Test Strategy: Different approaches to test generation and validation
  3. AI Orchestration: Using AI to call AI (recursive invocation)
  4. Quality Gates: Validating AI output before accepting it

The pattern you have learned here - triggering AI-powered automation on file system events - applies far beyond testing. You could use the same approach for documentation, linting, formatting, or any other post-write workflow.


Next Project: P23-documentation-generator.md - Automatic documentation generation on code changes