Unit Test Coverage

Goal: Achieve 80% coverage on new code changes with minimal, high-quality tests.

aadorian updated 7mo ago
Claude CodeGeneric
View source ↗
# PR Unit Test Coverage Agent

**Goal**: Achieve 80% coverage on new code changes with minimal, high-quality tests.

## ⚠️ CRITICAL: Fix Broken Tests First!

PR changes often break existing tests. The coverage:analyze script detects these automatically.

## Core Principles

1. **Test Stability First**: All tests must pass before measuring coverage
2. **Follow Guidelines**: AAA pattern, strong assertions per `unit-testing-guidelines.mdc`
3. **Quality over Quantity**: Write fewer tests with better assertions
4. **Target New Code**: Focus on `newCodeCoverage` metric (SonarCloud-style)
5. **Reuse Infrastructure**: Use existing mocks and patterns
6. **No Human Interaction**: Fully automated feedback loop

## Workflow

### Step 0: Load Context (MANDATORY)

Confirm understanding by stating:

- "I will follow AAA pattern (Arrange, Act, Assert)"
- "I will write one behavior per test"
- "I will use strong assertions (toBeOnTheScreen, not toBeDefined)"
- "I will fix broken tests before adding coverage"
- "I will target 80% newCodeCoverage on changed lines"
- "I will search for and reuse existing domain-specific mocks"
- "I will NOT mock utility functions, simple logic, or pure functions"

### Step 1-6: Execute Coverage Loop

```bash
# 1. Analyze current state
yarn coverage:analyze

# 2. Check for failed tests FIRST
cat scripts/reports/coverage-report-*.json | jq '.failedTests'

# 3. Fix all failing tests (if any)
# 4. Re-run coverage:analyze to confirm tests pass
# 5. NOW focus on coverage improvements
# 6. Repeat until newCodeCoverage >= 80%

Decision Tree

flowchart TD
    A[Start] --> B[Load and Confirm Guidelines]
    B --> C[Run coverage:analyze]
    C --> D{Failed Tests?}
    D -->|Yes| E[Fix ALL broken tests first]
    E --> F[Re-run coverage:analyze]
    F --> D
    D -->|No| G{newCodeCoverage less than 80%?}
    G -->|No| H[Success]
    G -->|Yes| I{Can improve existing tests?}
    I -->|Yes| J[Add assertions to existing tests]
    I -->|No| K[Create minimal new tests]
    J --> L[Re-run coverage:analyze]
    K --> L
    L --> M{Coverage Met?}
    M -->|No| I
    M -->|Yes| H

Handling Failed Tests (Priority #1)

// coverage-report.json provides:
{
  "failedTests": [{
    "file": "usePerps.test.tsx",
    "error": "Cannot read property 'data' of undefined",
    "command": "npx jest usePerps.test.tsx --no-coverage"
  }]
}

Common Fixes:

  • Mock signature mismatch → Update return values
  • Import path changes → Fix moved files
  • Type errors → Use jest.mocked()
  • Missing dependencies → Add new mocks

❌ FORBIDDEN Patterns

// NEVER submit tests with:
as any                                          // Use proper types
console.log()                                   // Remove ALL
// @ts-ignore                                   // Fix type issues
toBeDefined()                                   // Use specific assertions
toMatchSnapshot()                               // Test behavior not snapshots

// NEVER test implementation details:
expect(Logger.error).toHaveBeenCalledWith('msg')  // Don't test logs
expect(console.warn).toHaveBeenCalled()           // Test behavior instead

Test WHAT the system does (behavior), not HOW it logs (implementation).

Mocking Guidelines

❌ DO NOT Mock:

  • Utility functions (test actual logic)
  • Pure functions (no side effects)
  • Simple calculations
  • Data transformations

✅ DO Mock:

  • External APIs/network calls
  • React Native modules
  • Redux store/selectors (reuse existing)
  • Complex dependencies with side effects

Find Existing Mocks First:

find . -path "*/__mocks__/*" -name "*.ts" | grep -i feature
grep -r "createMock" app/components/
grep -r "jest.mock.*YourFeature" app/**/*.test.ts

Test Quality Standards

1. AAA Pattern

it('displays error when invalid', () => {
  // Arrange
  const input = '';
  // Act
  const result = validate(input);
  // Assert
  expect(result).toBe(false);
});

2. One Behavior Per Test

// ✅ Good: Single behavior
it('returns true for valid email', () => {
  expect(isEmail('a@b.com')).toBe(true);
});

// ❌ Bad: Multiple behaviors
it('validates and updates and shows error', () => {
  /* too much */
});

3. Strong Assertions

// ✅ Good
expect(screen.getByText('Error')).toBeOnTheScreen();

// ❌ Bad
expect(something).toBeDefined();

4. Parameterized Tests for Efficiency

it.each(['small', 'medium', 'large'] as const)('renders %s size', (size) => {
  const { getByTestId } = render(<Button size={size} />);
  expect(getByTestId('button')).toHaveStyle({ fontSize: sizes[size] });
});

Coverage Strategy: Improve Existing Tests First

Before creating new tests, enhance existing ones:

// BEFORE: Weak test with missed coverage
it('handles error', async () => {
  mockAPI.fail();
  render(<Component />);
  expect(screen.getByText('Error')).toBeOnTheScreen();
});

// AFTER: Same test covering more lines
it('handles error', async () => {
  mockAPI.fail();
  render(<Component />);
  expect(screen.getByText('Error')).toBeOnTheScreen();
  expect(screen.queryByTestId('loading')).toBeNull(); // +2 lines
  expect(screen.getByTestId('retry-button')).toBeEnabled(); // +3 lines
  expect(mockAnalytics.track).toHaveBeenCalledWith('error_shown'); // +1 line
});

Principle: One test with 5 assertions > 5 tests with 1 assertion each

Test Patterns

React Component

describe('Component', () => {
  it('handles user interaction', () => {
    const onPress = jest.fn();
    const { getByTestId } = render(<Component onPress={onPress} />);
    fireEvent.press(getByTestId('button'));
    expect(onPress).toHaveBeenCalled();
    expect(getByTestId('result')).toBeOnTheScreen();
  });
});

React Hook

describe('useHook', () => {
  it('returns expected values', () => {
    const { result } = renderHook(() => useHook());
 

Maintain Unit Test Coverage?

Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.

[Unit Test Coverage on getagentictools](https://getagentictools.com/loops/aadorian-pr-unit-test-coverage-agent?ref=badge)
npx agentictools info loops/aadorian-pr-unit-test-coverage-agent

The second line is the CLI lookup for this page — handy in READMEs and docs.