testing-patterns
Patrons et stratégies de test complets pour les projets JavaScript/TypeScript. Couvre les tests unitaires, d'intégration et E2E, les stratégies de mocking, l'organisation des tests et les anti-patrons courants. À utiliser quand l'utilisateur veut écrire des tests, améliorer la couverture de tests, établir une stratégie de test ou corriger des tests instables.
What this skill does
# Testing Patterns & Strategies
Comprehensive guide to testing JavaScript/TypeScript applications. Complements the `tdd` skill (which focuses on workflow) by providing concrete patterns, strategies, and anti-patterns.
## When to Use This Skill
Activate when the user:
- Wants to write tests for existing or new code
- Needs to establish a testing strategy for a project
- Wants to improve test coverage or reliability
- Has flaky or brittle tests
- Asks about mocking, stubbing, or test doubles
- Needs guidance on unit vs integration vs E2E tests
## Testing Pyramid
```
/ E2E \ Few, slow, high confidence
/----------\
/ Integration \ Moderate count, medium speed
/----------------\
/ Unit \ Many, fast, focused
/____________________\
```
**Rule of thumb:** ~70% unit, ~20% integration, ~10% E2E. Adjust per project — legacy codebases often benefit from more integration tests initially.
## 1. Unit Testing Patterns
### Test Structure: AAA (Arrange, Act, Assert)
```typescript
describe('calculateDiscount', () => {
it('should apply 10% discount for orders above 100€', () => {
// Arrange
const order = { total: 150, items: ['item1', 'item2'] }
// Act
const result = calculateDiscount(order)
// Assert
expect(result).toBe(135)
})
})
```
### Naming Convention
Use descriptive names that read as specifications:
```typescript
// GOOD — describes behavior
it('should return 401 when token is expired')
it('should retry 3 times before failing')
it('should trim whitespace from patient name')
// BAD — describes implementation
it('should call validateToken')
it('should set retryCount to 3')
it('should use .trim()')
```
### Parameterized Tests (Table-Driven)
```typescript
describe('parseHL7Date', () => {
it.each([
['20260315', new Date(2026, 2, 15)],
['20260315120000', new Date(2026, 2, 15, 12, 0, 0)],
['', null],
['invalid', null],
])('should parse "%s" to %s', (input, expected) => {
expect(parseHL7Date(input)).toEqual(expected)
})
})
```
### Testing Error Cases
Always test the unhappy path:
```typescript
describe('PatientService.findById', () => {
it('should throw NotFoundError for unknown patient ID', async () => {
await expect(service.findById('UNKNOWN'))
.rejects.toThrow(NotFoundError)
})
it('should throw ValidationError for empty ID', async () => {
await expect(service.findById(''))
.rejects.toThrow(ValidationError)
})
})
```
### Testing Async Code
```typescript
// Promises
it('should resolve with patient data', async () => {
const patient = await fetchPatient('PAT123')
expect(patient.name).toBe('DUPONT')
})
// Event emitters
it('should emit "transfer" event on unit change', (done) => {
emitter.on('transfer', (data) => {
expect(data.unit).toBe('CARDIO')
done()
})
service.transferPatient('PAT123', 'CARDIO')
})
```
## 2. Integration Testing Patterns
Integration tests verify that components work together correctly.
### Database Integration Tests
```typescript
describe('PatientRepository', () => {
let db: TestDatabase
beforeAll(async () => {
db = await TestDatabase.create() // Real DB, not mock
await db.migrate()
})
afterEach(async () => {
await db.truncate() // Clean between tests
})
afterAll(async () => {
await db.destroy()
})
it('should persist and retrieve a patient', async () => {
const repo = new PatientRepository(db.connection)
await repo.create({
id: 'PAT123',
name: 'DUPONT',
birthDate: '1975-03-15',
})
const found = await repo.findById('PAT123')
expect(found).toMatchObject({
id: 'PAT123',
name: 'DUPONT',
})
})
})
```
### API Integration Tests
```typescript
describe('POST /api/patients', () => {
it('should create a patient and return 201', async () => {
const response = await request(app)
.post('/api/patients')
.send({ name: 'DUPONT', birthDate: '1975-03-15' })
.set('Authorization', `Bearer ${validToken}`)
expect(response.status).toBe(201)
expect(response.body.data.id).toBeDefined()
})
it('should return 400 for missing required fields', async () => {
const response = await request(app)
.post('/api/patients')
.send({}) // Missing name
.set('Authorization', `Bearer ${validToken}`)
expect(response.status).toBe(400)
expect(response.body.errors).toContainEqual(
expect.objectContaining({ field: 'name' })
)
})
it('should return 401 without authentication', async () => {
const response = await request(app)
.post('/api/patients')
.send({ name: 'DUPONT' })
expect(response.status).toBe(401)
})
})
```
### Message Processing Integration Tests
Particularly relevant for healthcare message processing (HPK, HL7):
```typescript
describe('HPK Message Pipeline', () => {
it('should parse, validate, and transform an ID|M1 message', async () => {
const rawMessage = 'ID|M1|C|HEXAGONE|20260122120000|USER001|PAT123|DUPONT|JEAN|19750315|M'
const result = await pipeline.process(rawMessage)
expect(result.type).toBe('ID')
expect(result.code).toBe('M1')
expect(result.patient.name).toBe('DUPONT')
expect(result.valid).toBe(true)
})
})
```
## 3. E2E Testing Patterns
E2E tests validate complete user workflows through the real application.
### Page Object Pattern
```typescript
class PatientListPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/patients')
}
async search(query: string) {
await this.page.fill('[data-testid="search-input"]', query)
await this.page.click('[data-testid="search-button"]')
}
async getResults() {
return this.page.locator('[data-testid="patient-row"]').allTextContents()
}
async clickPatient(name: string) {
await this.page.click(`text=${name}`)
}
}
// Usage in test
test('should find patient by name', async ({ page }) => {
const patientList = new PatientListPage(page)
await patientList.goto()
await patientList.search('DUPONT')
const results = await patientList.getResults()
expect(results).toContain('DUPONT JEAN')
})
```
### Data-testid Convention
Use `data-testid` attributes for test selectors, never CSS classes or element structure:
```typescript
// GOOD — stable selector
await page.click('[data-testid="submit-admission"]')
// BAD — brittle, breaks on style/structure changes
await page.click('.btn.btn-primary.submit')
await page.click('form > div:nth-child(3) > button')
```
### Visual Regression Testing
```typescript
test('patient dashboard matches snapshot', async ({ page }) => {
await page.goto('/dashboard')
await page.waitForLoadState('networkidle')
await expect(page).toHaveScreenshot('dashboard.png', {
maxDiffPixelRatio: 0.01,
})
})
```
## 4. Mocking Strategies
### When to Mock
| Mock | Don't Mock |
|------|------------|
| External HTTP APIs | Your own database (use real test DB) |
| Time/Date (for determinism) | Internal collaborators between modules |
| File system (when impractical) | Simple utility functions |
| Third-party services (payment, email) | Data transformations |
| Environment-specific features | Business logic |
### Mock vs Stub vs Spy
```typescript
// STUB — returns a fixed value
const getPatient = vi.fn().mockResolvedValue({ id: 'PAT123', name: 'DUPONT' })
// SPY — observes calls without changing behavior
const logSpy = vi.spyOn(logger, 'info')
await service.admitPatient(data)
expect(logSpy).toHaveBeenCalledWith('Patient admitted', { id: 'PAT123' })
// MOCK — replaces the entire module
vi.mock('./emailService', () => ({
sendAdmissionNotification: vi.fn().mockResolvedValue(true),
}))
```
### MSW for HTTP Mocking
Prefer MSW (Mock Service Worker) over manual fetch mocking:
```typescript
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
const server = setupServer(
http.get('/api/patients/:id', ({Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.