# Test

> Write Unit Tests Skill

- Skill: `valasubramanian-kr/test` (Agent Skill)
- Install (CLI): `npx skillmds@latest add valasubramanian-kr/test`
- Raw SKILL.md: https://api.skillmd.com/api/skills/valasubramanian-kr/test/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: valasubramanian-kr (https://skillmd.com/u/valasubramanian-kr)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/valasubramanian-kr/test

---


# Write Unit Tests Skill

## Purpose

Write comprehensive unit tests for the implemented code following project-specific testing patterns.

## Usage

```bash
/test
```

(Must be run after `/code`)

## What This Skill Does

1. **Load Current JIRA Issue**: Reads JIRA issue details from `current-issue.md`
2. **Load Implementation Plan**: Reads detailed plan from `implementation-plan.md`
3. **Load Implementation Log**: Reads what was implemented from `implementation-log.md`
4. **Identify Test Requirements**: Determines what needs to be tested
5. **Write Unit Tests**: Creates comprehensive test files
6. **Log Tests Created**: Records all test files to `unit-tests.md`

## JIRA Issue Context

The JIRA issue details should be located in the most recent directory under `workflow/jira-to-github/*/current-issue.md`. You will need to find and read this file in Step 1.

## Implementation Plan Context

The implementation plan should be located in the most recent directory under `workflow/jira-to-github/*/implementation-plan.md`. You will need to find and read this file in Step 1.

## Implementation Log Context

The implementation log should be located in the most recent directory under `workflow/jira-to-github/*/implementation-log.md`. You will need to find and read this file in Step 1.

## Instructions

You are writing unit tests for the implemented code. This skill spawns a sub-agent to create comprehensive test files following project-specific testing patterns.

**CRITICAL**: Your ONLY job is to:
1. Write unit tests for the implemented code
2. Spawn a 'general-purpose' sub-agent using the Task tool
3. Report the agent's results to the user

**DO NOT**:
- Modify implementation code
- Run tests (that's /validate's job)
- Push code to github
- If the sub-agent fails, DO NOT retry the work yourself - report the failure to the user

### Step 1: Validate Prerequisites

Verify that prerequisites are met:

```bash
# Verify JIRA issue details exists
JIRA_DIR=$(ls -td workflow/jira-to-github/*/ 2>/dev/null | head -1)
if [ ! -f "$JIRA_DIR/current-issue.md" ]; then
  echo "❌ No JIRA Issue found. Run /pull first."
  exit 1
fi

# Verify implementation plan exists
if [ ! -f "$JIRA_DIR/implementation-plan.md" ]; then
  echo "❌ No implementation plan found. Run /plan first."
  exit 1
fi

# Verify implementation log exists
if [ ! -f "$JIRA_DIR/implementation-log.md" ]; then
  echo "❌ No implementation found. Run /code first."
  exit 1
fi
```

### Step 2: Spawn Test Writing Agent

Use the Task tool to spawn a `general-purpose` agent to write unit tests autonomously:

**Agent Prompt**:
```
Write comprehensive unit tests for the implemented code based on the implementation plan and implementation log.

CONTEXT FILES LOADED:
- Target repo: workflow/jira-to-github/*/target-repo.md
- Current issue: workflow/jira-to-github/*/current-issue.md
- Implementation plan: workflow/jira-to-github/*/implementation-plan.md
- Implementation log: workflow/jira-to-github/*/implementation-log.md
- Previous unit tests log (if exists): workflow/jira-to-github/*/unit-tests.md

REPO CONTEXT LOADING:
Before writing tests, load repo-specific testing patterns:
1. Read `target-repo.md` to find the repo path and profile path
2. Read the repo profile (e.g., `context/repos/esperanto.md`) to get `Context Files` pointers
3. Read the repo's own CLAUDE.md at `<repo-path>/CLAUDE.md` for testing patterns and conventions
4. If CLAUDE.md lacks testing patterns or has only repo-wide patterns:
   - Read the module context file at `<repo-path>/<primary-module-path>` (as specified in profile)
   - Look for module-specific testing conventions, mock patterns, test commands
5. Apply these patterns throughout test writing (test file naming, mock strategy, framework usage)
6. If `target-repo.md` is not found, fall back to patterns discovered during codebase exploration

Tasks:

1. Check for previous test implementation:
   - Check if unit-tests.md exists in the workflow directory
   - If it exists, this is a RE-IMPLEMENTATION after plan/code updates:
     * Read the previous test log to understand what tests were already written
     * Read the current implementation log to see what's currently implemented
     * Identify what changed in the implementation
     * Update or add new tests based on implementation changes
     * Append to unit tests log noting this is a revision
   - If it doesn't exist, this is a FIRST-TIME test implementation:
     * Proceed with full test implementation as planned

2. Read and analyze the implementation:
   - Review implementation-log.md to see what files were created/modified
   - Review implementation-plan.md to understand the requirements
   - Identify all functions, components, hooks, and modules that need tests
   - Review current-issue.md to understand the feature being tested

3. Find and analyze existing test patterns:
   - Use testing patterns from repo CLAUDE.md or module context file as primary reference
   - Search for existing test files in the codebase (*.spec.ts, *.test.ts, *.spec.tsx, *.test.tsx) only if repo context files don't cover testing patterns
   - Identify testing frameworks and libraries used (Jest, React Testing Library, etc.)
   - Find common test utilities and helpers
   - Review mocking patterns (e.g., "mock selectors, NOT useSelector" in Esperanto)
   - Understand test file organization and naming conventions from repo context
   - Find existing mock data or fixtures

4. Identify what needs to be tested:

   **CRITICAL: Write MINIMAL, FOCUSED tests that cover logical changes and meet 80%+ coverage.**
   - Prioritize: business logic > edge cases > error handling > UI interactions
   - Skip testing: simple getters/setters, pure rendering without logic, trivial pass-through props
   - Write one focused test per critical path, avoid exhaustive scenario coverage
   - Maximize coverage with minimum number of tests

   **For Components**:
   - Component renders correctly (only if complex conditional rendering exists)
   - Props are handled properly (only non-trivial prop logic)
   - User interactions work (only critical interactions with business logic, not simple clicks)
   - Conditional rendering based on state/props (focus on business logic branches)
   - Error states (only if they trigger logic, not just display changes)
   - Skip: simple rendering, basic prop passing, trivial UI interactions

   **For Hooks**:
   - Initial state is correct
   - State updates work as expected
   - Side effects trigger properly
   - Cleanup functions work
   - Edge cases and error handling

   **For Utilities/Functions**:
   - Happy path scenarios
   - Edge cases (empty inputs, null, undefined)
   - Error conditions
   - Input validation
   - Return values are correct

   **For API/Services**:
   - Successful API calls
   - Error handling (network errors, 4xx, 5xx)
   - Request parameters are correct
   - Response parsing
   - Retry logic if applicable

   **For Stores/State Management**:
   - Initial state
   - Actions/reducers modify state correctly
   - Selectors return correct values
   - Async actions work properly
   - Side effects trigger correctly

5. Write unit tests for each file:

   **Test File Location**:
   - Follow project conventions (e.g., `__tests__` directory or `.spec.ts` suffix)
   - Typically place test files adjacent to source files or in a tests directory
   - Examples:
     * `src/components/Button.tsx` → `src/components/Button.spec.tsx`
     * `src/hooks/useAuth.ts` → `src/hooks/useAuth.spec.ts`

   **Test File Structure**:
   ```typescript
   import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
   import { render, screen, fireEvent, waitFor } from '@testing-library/react';
   // Import what you're testing
   import { ComponentOrFunction } from './path';
   // Import mocks and utilities
   import { mockData } from './mocks';

   describe('ComponentOrFunction', () => {
     // Setup and teardown
     beforeEach(() => {
       // Setup before each test
     });

     afterEach(() => {
       // Cleanup after each test
       jest.clearAllMocks();
     });

     describe('Feature group 1', () => {
       it('should handle happy path scenario', () => {
         // Arrange
         const props = { /* test props */ };

         // Act
         const result = ComponentOrFunction(props);

         // Assert
         expect(result).toBe(expectedValue);
       });

       it('should handle edge case', () => {
         // Test edge case
       });

       it('should handle error condition', () => {
         // Test error handling
       });
     });

     describe('Feature group 2', () => {
       // More tests...
     });
   });
   ```

   **Component Testing**:
   ```typescript
   import { render, screen, fireEvent } from '@testing-library/react';
   import { Button } from './Button';

   describe('Button', () => {
     it('should render with text', () => {
       render(<Button>Click me</Button>);
       expect(screen.getByText('Click me')).toBeInTheDocument();
     });

     it('should call onClick when clicked', () => {
       const onClick = jest.fn();
       render(<Button onClick={onClick}>Click</Button>);
       fireEvent.click(screen.getByText('Click'));
       expect(onClick).toHaveBeenCalledTimes(1);
     });

     it('should be disabled when disabled prop is true', () => {
       render(<Button disabled>Click</Button>);
       expect(screen.getByRole('button')).toBeDisabled();
     });
   });
   ```

   **Hook Testing**:
   ```typescript
   import { renderHook, act } from '@testing-library/react';
   import { useCounter } from './useCounter';

   describe('useCounter', () => {
     it('should initialize with default value', () => {
       const { result } = renderHook(() => useCounter());
       expect(result.current.count).toBe(0);
     });

     it('should increment count', () => {
       const { result } = renderHook(() => useCounter());
       act(() => {
         result.current.increment();
       });
       expect(result.current.count).toBe(1);
     });
   });
   ```

   **API/Service Testing**:
   ```typescript
   import { apiService } from './apiService';
   import { mockFetch } from './mocks';

   describe('apiService', () => {
     beforeEach(() => {
       global.fetch = jest.fn();
     });

     it('should fetch data successfully', async () => {
       (global.fetch as jest.Mock).mockResolvedValueOnce({
         ok: true,
         json: async () => ({ data: 'test' }),
       });

       const result = await apiService.getData();
       expect(result).toEqual({ data: 'test' });
     });

     it('should handle network errors', async () => {
       (global.fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'));

       await expect(apiService.getData()).rejects.toThrow('Network error');
     });
   });
   ```

   **Best Practices**:
   - Use descriptive test names (should/when/given format) - these replace comments
   - Do NOT add comments in test files - test names should be self-documenting
   - Do NOT add file headers or JIRA references
   - Follow AAA pattern: Arrange, Act, Assert
   - Test one thing per test case
   - Use meaningful variable names in tests
   - Mock external dependencies
   - Don't test implementation details, test behavior
   - Cover critical paths to achieve 80%+ code coverage with minimal tests
   - Focus on quality over quantity - fewer, well-targeted tests
   - Keep tests simple and readable
   - Use test utilities and helpers to reduce duplication
   - Group related tests with describe blocks

   **Mocking Guidelines**:
   - Mock external APIs and services
   - Mock timers when testing time-dependent code
   - Mock navigation/routing
   - Mock browser APIs (localStorage, sessionStorage, etc.)
   - Use jest.fn() for function mocks
   - Use jest.mock() for module mocks
   - Reset mocks between tests

   **Documentation Guidelines for Test Log**:
   - Use tables to summarize test coverage (Component | Tests | Key Scenarios)
   - Keep test file descriptions concise with key scenarios only
   - Reference mock files (e.g., "See src/mocks/index.ts → MOCK_DATA") instead of showing full mock structures
   - Use bullet points for testing patterns, not verbose paragraphs
   - Avoid listing every individual test - summarize by component/module
   - Focus on WHAT is tested, not HOW (avoid code examples in log)
   - Target: Keep unit-tests.md under 12 KB (aim for 10-12 KB)

6. Create or update unit tests log:

   **For first-time test implementation**:
   ```markdown
   # Unit Tests: <JIRA-KEY>

   ## Summary

   Brief description of what functionality is being tested (1-2 sentences).

   ## Test Coverage Summary

   | Component/Module | Tests | Key Scenarios |
   |------------------|-------|---------------|
   | Button | 12 | Rendering, click handlers, disabled/loading states, a11y |
   | useAuth hook | 8 | Login/logout flows, token refresh, error handling |
   | API Service | 15 | Success/error responses, network errors, timeout, retry logic |

   **Total**: 35 tests across 3 files | **Expected Coverage**: ~85%

   ## Test Files Created/Updated

   ### Component Tests
   **File**: `src/components/Button/__tests__/Button.spec.tsx`
   - **Tests Added**: 12
   - **Coverage**: Button component rendering and interaction
   - **Key Scenarios**: Props handling, click events, state variations, accessibility

   ### Hook Tests
   **File**: `src/hooks/__tests__/useAuth.spec.ts`
   - **Tests Added**: 8
   - **Coverage**: Authentication hook
   - **Key Scenarios**: Login/logout flows, token management, error handling

   ### Service Tests
   **File**: `src/services/__tests__/api.spec.ts`
   - **Tests Added**: 15
   - **Coverage**: API service layer
   - **Key Scenarios**: HTTP requests, error handling, retry logic

   ## Testing Patterns

   - Props-based testing for component isolation
   - Hook mocking for state management
   - React Testing Library best practices
   - Jest mocks for external dependencies

   ## Mock Data

   Mock data: See `src/mocks/index.ts`
   - User data: `MOCK_USER_DATA`
   - API responses: `MOCK_API_RESPONSES`

   ## Next Steps

   1. Run `/validate` to execute tests and check coverage
   2. Review test output for any failures
   3. Verify coverage meets thresholds

   ---
   *Tests written: <timestamp>*
   ```

   **For re-implementation after code updates**:
   ```markdown
   # Unit Tests: <JIRA-KEY>

   [... previous content remains ...]

   ---

   ## Revision: <timestamp>

   ### Changes
   Implementation updated: <brief summary of changes>

   ### Test Updates

   | File | Change Type | Tests Added/Modified |
   |------|-------------|---------------------|
   | Button.spec.tsx | Modified | +3 new, 2 updated (error state, click handler, a11y) |
   | NewFeature.spec.tsx | Created | +10 new tests |
   | OldComponent.spec.tsx | Removed | Component removed from codebase |

   ### Updated Coverage Summary
   **Total**: 48 tests (+13) across 4 files (+1) | **Expected Coverage**: ~87%

   ### Next Steps
   1. Run `/validate` to execute updated tests
   2. Verify no regressions
   3. Check coverage thresholds

   ---
   *Tests updated: <timestamp>*
   ```

7. Save unit tests log to: workflow/jira-to-github/<JIRA-NUMBER>/unit-tests.md
   - For first-time: Create new file
   - For revisions: Append to existing file

8. Display summary to user showing:
   - Count of test files created
   - Count of tests written
   - Expected coverage percentage
   - Testing patterns used
   - Unit tests log file location
   - Next step: Run /validate

ERROR HANDLING:
- If implementation not found: Verify /code was run first
- If test file already exists: Update or enhance existing tests
- If testing framework not found: Use Jest/React Testing Library as defaults
- Log all errors to: workflow/jira-to-github/<JIRA-NUMBER>/errors.log
```

### Step 3: Monitor Agent Progress

The agent will run autonomously. When complete, it will have:
- Read and understood the implementation plan and log
- Analyzed existing test patterns in the codebase
- Created comprehensive unit test files
- Followed project-specific testing conventions
- Created unit tests log in workflow/jira-to-github/<JIRA-NUMBER>/unit-tests.md
- Displayed summary to user

## Error Handling

If errors occur:
1. Log error to `workflow/jira-to-github/<JIRA-NUMBER>/errors.log`
2. Display user-friendly message
3. Do NOT proceed to validation if test writing failed

Common errors:
- **Implementation not found**: Verify /code was run first
- **Test file conflicts**: Update existing tests instead of overwriting
- **Missing test utilities**: Install required testing libraries
- **Import errors**: Verify import paths match project structure

## Examples

Example 1: Writing tests for new component
```bash
/test
# Creates Button.spec.tsx
# Writes 12 tests covering all scenarios
# Creates mock data files
# Logs to unit-tests.md
```

Example 2: Writing tests for API service
```bash
/test
# Creates apiService.spec.ts
# Writes tests for success/error cases
# Mocks fetch calls
# Tests retry logic
```

Example 3: Re-writing tests after code update
```bash
# Initial workflow
/pull DRT-17270
/plan
/review  # User approves
/branch
/code    # Initial implementation
/test    # Initial tests

# User realizes code needs changes
/review "Add error boundary"
# Plan is revised, code is updated
/code    # Reads updated plan, applies changes

# Re-run test to update tests for new code
/test    # Reads updated implementation, adds/updates tests
# Unit tests log shows revision with changes
```

Example 4: Full workflow with tests
```bash
/pull DRT-17270
/plan
/review
/branch
/code       # Implements feature
/test       # Writes unit tests
/validate   # Runs tests, linting, type checking
/push       # Creates PR
```

