test-review
Review existing tests for completeness, quality issues, and common mistakes
When & Why to Use This Skill
This Claude skill automates the evaluation of software test suites to ensure comprehensive coverage, high quality, and adherence to industry best practices. It systematically identifies missing test cases for validation schemas, API procedures, and UI components while flagging common anti-patterns such as fragile selectors, improper mock usage, and asynchronous testing errors.
Use Cases
- Pre-PR Test Audit: Use the skill to review test files before submitting a Pull Request, ensuring that new features have adequate coverage and no debugging code (like .only or console.log) is left behind.
- Improving Test Reliability: Identify and refactor flaky tests by replacing fragile CSS selectors with semantic locators and fixing improper async/await implementations.
- Edge Case Identification: Automatically detect missing validation tests for boundary conditions, such as empty strings, invalid formats, or unauthorized API access attempts.
- Framework-Specific Optimization: Audit Vitest and Playwright suites to ensure proper mock resetting, realistic data simulation, and efficient use of built-in assertions.
| name | test-review |
|---|---|
| description | Review existing tests for completeness, quality issues, and common mistakes |
Test Review
Review tests for a feature to catch coverage gaps and quality issues before PR.
Instructions
- Identify the feature being tested (from user or recent git changes)
- Find related source files: validations, tRPC routers, components, pages
- Find related test files: vitest and playwright
- Check coverage gaps - what's missing?
- Check test quality - are assertions meaningful?
- Check for common mistakes
- Report findings with file:line references
Coverage Gaps
Validation Schemas
- Happy path with valid input
- Each required field missing
- Invalid formats (email, date, etc.)
- Edge cases (empty string, zero, negative numbers)
- Type coercion if applicable
tRPC Procedures
- Success case returns expected shape
- Error case (not found, unauthorized)
- Input validation rejects bad data
- Side effects happen (database updated, email sent)
React Components
- Renders without crashing
- Displays data correctly
- Loading state
- Error state
- User interactions (click, submit, type)
- Accessibility (keyboard nav, screen reader)
E2E (Playwright)
- Happy path flow start to finish
- Form validation errors shown to user
- Error recovery (retry, go back)
- Empty states
Test Quality
Meaningful Assertions
// Bad - just checks truthy
expect(result).toBeTruthy()
// Good - checks specific value
expect(result.id).toBe('123')
expect(result.items).toHaveLength(3)
Independent Tests
- No shared mutable state between tests
- Each test sets up its own data
- Order of tests doesn't matter
Realistic Mocks
// Bad - mock returns nothing useful
const mockFetch = jest.fn()
// Good - mock returns realistic data
const mockFetch = jest.fn().mockResolvedValue({
id: '123',
name: 'Test User',
createdAt: new Date(),
})
Error Testing
// Bad - just checks it throws
expect(() => fn()).toThrow()
// Good - checks specific error
expect(() => fn()).toThrow('User not found')
expect(() => fn()).toThrow(NotFoundError)
Common Mistakes
General
- No
.onlyor.skipleft in code - No
console.login tests - No hardcoded IDs that won't exist in CI
- No time-dependent tests without mocking
Date.now()
Async Issues
// Bad - no await, test passes before async completes
it('creates user', () => {
createUser({ name: 'Test' })
expect(db.users).toHaveLength(1)
})
// Good - awaits async operation
it('creates user', async () => {
await createUser({ name: 'Test' })
expect(db.users).toHaveLength(1)
})
Playwright-Specific
- Uses semantic locators (
getByRole,getByLabel) not CSS selectors - No
page.waitForTimeout()- usewaitForResponse,waitForSelector, orexpect().toBeVisible() - Tests don't depend on previous test's state
- Assertions check visible outcomes, not implementation
// Bad - fragile selector, arbitrary timeout
await page.click('.btn-submit')
await page.waitForTimeout(1000)
// Good - semantic locator, waits for outcome
await page.getByRole('button', { name: 'Submit' }).click()
await expect(page.getByText('Saved successfully')).toBeVisible()
Vitest-Specific
- Mocks are reset between tests (
vi.clearAllMocks()inbeforeEach) - No network calls without MSW or manual mocking
-
describeblocks group related tests logically
Output Format
## Test Review: [feature]
### Coverage Gaps
- validations/booking.ts has no tests for `updateBookingSchema`
- No E2E test for delete flow
- Missing error state test for BookingForm component
### Quality Issues
- tests/vitest/booking.test.ts:23 - assertion just checks truthy, verify specific value
- tests/vitest/booking.test.ts:45 - mock returns empty object, use realistic data
### Common Mistakes
- tests/playwright/booking.spec.ts:12 - uses `waitForTimeout(2000)`, use `waitForResponse` or assertion
- tests/vitest/booking.test.ts:67 - `.only` left in code
### Passed
- Validation schema has happy path + required field tests
- tRPC procedures test success and not-found cases
- No hardcoded IDs
- Semantic locators used throughout
Notes
- Focus on changed files first, but check related test files too
- Not every gap needs fixing - use judgment on risk vs effort
- Flag
.onlyand.skipas blockers - these break CI