@jjdenhertog/ai-driven-development
Version:
AI-driven development workflow with learning capabilities for Claude
279 lines (229 loc) • 8.24 kB
Markdown
---
name: "Testing Strategy and Preferences"
description: "Defines the testing approach, tools, and conventions for this project"
ai_instructions: |
When implementing tests:
1. Use Vitest as the primary test runner, not Jest
2. Write tests alongside implementation (TDD when appropriate)
3. Focus on testing behavior, not implementation details
4. Aim for 80%+ coverage on new code
5. Use React Testing Library for component tests
6. Use Playwright for E2E tests
---
# Testing Strategy and Preferences
<ai-context>
This guide defines the testing approach for Next.js applications. We use Vitest for unit/component testing
and Playwright for E2E testing. Tests should focus on behavior and user interactions rather than
implementation details. AI should write tests alongside feature implementation.
</ai-context>
## Testing Philosophy
<ai-rules>
- WRITE tests for all new features and bug fixes
- FOCUS on testing behavior, not implementation
- USE data-testid attributes for reliable element selection
- MOCK external dependencies and API calls
- KEEP tests fast and independent
</ai-rules>
- Follow Test-Driven Development (TDD) practices with a pragmatic approach
- Write tests alongside features, not necessarily before (Red-Green-Refactor when appropriate)
- Focus on behavior testing over implementation details
- Aim for 80%+ test coverage for new code
## Testing Stack
### Unit & Component Testing
- **Framework**: Vitest (preferred) or Jest
- **Component Testing**: React Testing Library
- **Assertion Library**: @testing-library/jest-dom
- **Coverage Tool**: Vitest coverage (c8) or Jest coverage
### E2E Testing
- **Framework**: Playwright
- **Browser Testing**: Chromium, Firefox, WebKit
- **Mobile Testing**: Mobile viewports in Playwright
### API Testing
- **Mocking**: Mock Service Worker (MSW)
- **API Client Testing**: Supertest or built-in fetch testing
## Testing Commands
```json
{
"scripts": {
"test": "vitest",
"test:watch": "vitest --watch",
"test:coverage": "vitest --coverage",
"test:ui": "vitest --ui",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug",
"test:all": "npm run test && npm run test:e2e"
}
}
```
```
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx
│ │ └── Button.stories.tsx
├── app/
│ ├── api/
│ │ └── users/
│ │ ├── route.ts
│ │ └── route.test.ts
tests/
├── auth.setup.ts
├── home.spec.ts
└── fixtures/
```
<validation-schema>
Test File Names:
- ✅ Button.test.tsx (unit/component tests)
- ✅ route.test.ts (API route tests)
- ✅ login.spec.ts (E2E tests)
- ❌ Button.spec.tsx (wrong extension for unit tests)
- ❌ test-button.tsx (wrong naming pattern)
Test Descriptions:
- ✅ "should display error message when form is invalid"
- ✅ "should redirect to dashboard after login"
- ❌ "test form validation" (not behavior-focused)
- ❌ "works correctly" (too vague)
</validation-schema>
- Test files: `*.test.ts(x)` for unit/component tests
- E2E files: `*.spec.ts` for Playwright tests
- Test descriptions: Use behavior-focused language
- ✅ "should display error message when form is invalid"
- ❌ "test form validation"
<code-template name="component-test">
```typescript
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from './Button'
describe('Button', () => {
it('should handle click events', async () => {
const user = userEvent.setup()
const handleClick = vi.fn()
render(<Button onClick={handleClick}>Click me</Button>)
await user.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
```
</code-template>
<code-template name="api-test">
```typescript
import { createMocks } from 'node-mocks-http'
import { POST } from './route'
describe('/api/users', () => {
it('should create a new user', async () => {
const { req } = createMocks({
method: 'POST',
body: { email: 'test@example.com' },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(201)
expect(data.email).toBe('test@example.com')
})
})
```
</code-template>
<code-template name="e2e-test">
```typescript
import { test, expect } from '@playwright/test'
test('user can complete signup flow', async ({ page }) => {
await page.goto('/signup')
await page.fill('[name="email"]', 'test@example.com')
await page.fill('[name="password"]', 'securepassword')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
await expect(page.getByText('Welcome')).toBeVisible()
})
```
</code-template>
1. **Before Implementation** (Optional TDD):
- Write E2E test for the user flow (failing test)
- Write component tests for key interactions (failing tests)
2. **During Implementation**:
- Implement feature to make tests pass
- Add unit tests for utility functions
- Add component tests for edge cases
3. **After Implementation**:
- Ensure all tests pass
- Check coverage (aim for 80%+)
- Add any missing edge case tests
### What to Test
<ai-decision-tree>
Should I write a test for this?
1. Is it a user interaction?
→ YES: Write a component test
→ NO: Continue to 2
2. Is it an API endpoint?
→ YES: Write an API test
→ NO: Continue to 3
3. Is it a utility function?
→ YES: Does it have complex logic?
→ YES: Write a unit test
→ NO: Skip if trivial
→ NO: Continue to 4
4. Is it a critical user flow?
→ YES: Write an E2E test
→ NO: Consider if testing adds value
</ai-decision-tree>
- **Always Test**:
- User interactions (clicks, form submissions)
- API endpoints (success and error cases)
- Business logic functions
- Error states and loading states
- Accessibility (ARIA labels, keyboard navigation)
- **Consider Testing**:
- Complex component state logic
- Custom hooks
- Utility functions with multiple cases
- Integration between components
- **Don't Test**:
- Third-party libraries
- Simple prop passing
- CSS styles (use visual regression if needed)
- Implementation details
## Coverage Requirements
- Minimum coverage for new code: 80%
- Critical paths must have 100% coverage
- Coverage reports should be reviewed in PR
## Performance Testing
- Keep test suites fast (< 5 minutes for full suite)
- Use test.concurrent() for independent tests
- Mock heavy operations and external services
- Run E2E tests in parallel when possible
## Continuous Integration
Tests should run automatically on:
- Every commit (unit tests)
- Every PR (all tests)
- Before deployment (all tests + smoke tests)
## Mock Data Strategy
- Use factories for consistent test data
- Keep test data realistic but minimal
- Use MSW for API mocking in component tests
- Maintain test fixtures separately
## Debugging Tests
- Use `test.only()` to isolate failing tests
- Enable Playwright trace viewer for E2E debugging
- Use `screen.debug()` for component test debugging
- Keep console.log statements out of committed tests
## Migration Path
For projects currently using Jest:
1. Keep Jest running while setting up Vitest
2. Migrate test by test (Vitest syntax is very similar)
3. Remove Jest once all tests are migrated
4. Update CI/CD pipelines
## Resources
- [Vitest Documentation](https://vitest.dev/)
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
- [Playwright Documentation](https://playwright.dev/)
- [MSW Documentation](https://mswjs.io/)