A test suite that only verifies the happy path does not test the code that actually runs when things go wrong. ISO 25010:2011 §6.5.5 requires reliability verification to cover failure modes. Missing error-path tests mean: API 404 and 500 responses produce untested UI states that may crash or show stale data; invalid inputs reach business logic that was never verified to reject them correctly; catch blocks that silently swallow errors pass code review because no test ever triggered them.
Low because untested error paths do not break current functionality but guarantee that failure scenarios — which happen in production — have never been automatically verified.
Add at least one negative test per critical unit. A minimal pattern alongside each success test:
describe('fetchUser', () => {
it('returns user data on success', async () => {
mockFetch.mockResolvedValue({ id: '1', name: 'Alice' })
const user = await fetchUser('1')
expect(user.name).toBe('Alice')
})
it('throws UserNotFoundError when 404', async () => {
mockFetch.mockRejectedValue(new Response(null, { status: 404 }))
await expect(fetchUser('nonexistent')).rejects.toBeInstanceOf(UserNotFoundError)
})
it('handles null user id gracefully', async () => {
await expect(fetchUser(null as unknown as string))
.rejects.toThrow('User ID is required')
})
})
Aim for at least 20% of tests to verify failure scenarios, not just success paths.
ID: code-quality-essentials.testing.error-handling
Severity: low
What to look for: Examine the test suite (if it exists) for tests that verify error scenarios, not just success paths. Look for: tests that pass invalid input to functions and assert the error thrown; tests that simulate API failures (network errors, 4xx/5xx responses) and assert the UI shows an error state; React Error Boundary tests; tests for null/empty/boundary values (empty arrays, zero, empty string, undefined). Also check the production code for the presence of try/catch blocks in async operations — missing error handling is a separate issue but correlated. A codebase with 100% "happy path" tests and no failure scenario tests is brittle.
Pass criteria: Enumerate all relevant code locations. Tests exist that cover failure scenarios — invalid input, network errors, null/empty edge cases. Error boundaries (if React) are tested. At least 20% of tests verify failure paths.
Fail criteria: All tests only cover success scenarios; no tests assert error states, throws, or empty results.
Skip (N/A) when: No test files exist.
Detail on fail: "All tests cover success paths only; no tests for invalid input, API failures, or null/empty edge cases"
Remediation: Add negative tests alongside each unit test. A minimal pattern:
describe('fetchUser', () => {
it('returns user data on success', async () => {
mockFetch.mockResolvedValue({ id: '1', name: 'Alice' })
const user = await fetchUser('1')
expect(user.name).toBe('Alice')
})
it('throws a UserNotFoundError when 404', async () => {
mockFetch.mockRejectedValue(new Response(null, { status: 404 }))
await expect(fetchUser('nonexistent')).rejects.toBeInstanceOf(UserNotFoundError)
})
it('handles null user id gracefully', async () => {
await expect(fetchUser(null as unknown as string))
.rejects.toThrow('User ID is required')
})
})