Unit Test Coverage
Goal: Achieve 80% coverage on new code changes with minimal, high-quality tests.
Claude CodeGeneric
# 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.