| name | testing-strategy |
|---|---|
| description | Vitest + Cypress + RTL — deterministic tests, ≥80% line / ≥70% branch coverage, ≥95% on security code, Three.js component testing |
| license | MIT |
Applies when writing unit tests, E2E tests, testing Three.js components, mocking dependencies, measuring coverage, diagnosing flakes, or preparing test data.
Aligned with Secure Development Policy §Unit Test Coverage & Quality.
- ≥ 80 % line / ≥ 70 % branch coverage across every module
- ≥ 95 % coverage on security-sensitive code (input validation, encoding, auth/authorization)
- Test pyramid — many unit, fewer integration, fewest E2E
- Behavior, not implementation — query by role/text; avoid internal-state checks
- AAA pattern — Arrange → Act → Assert; one behavior per test
- Mock external dependencies — APIs, timers, Three.js renderer — with proper types
- Cover error and edge cases — not just the happy path
- React Testing Library — prefer role/text queries; stable
data-testidallowed on intent - Test logic, not WebGL — for Three.js, test state/logic; mock the renderer
- Isolate tests — no shared mutable state;
vi.clearAllMocks()inbeforeEach - Descriptive names — "should <behavior> when <condition>"
- Deterministic — mock
Date.now,Math.random, timers; no real network - Fast — unit tests in ms, E2E in seconds; parallelize where safe
- No flaky tests — fix the root cause, never retry blindly
- No production / PII data in tests — anonymize or synthesize (per SDP §Test Data Protection)
- CI must pass — no merging red
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { HUD } from './HUD';
const mockGameState = {
score: 42, isPlaying: true, timeLeft: 30, combo: 0,
highScore: 100, targetSize: 0.5, level: 1, isNewHighScore: false,
targets: [] as unknown[], totalClicks: 10, successfulHits: 5,
} as const;
describe('HUD', () => {
beforeEach(() => { vi.clearAllMocks(); });
it('should display the current score', () => {
render(<HUD gameState={mockGameState} />);
expect(screen.getByText(/42/)).toBeInTheDocument();
});
});import { describe, it, expect } from 'vitest';
import { calculateLevel, getTargetCountForLevel } from './gameConfig';
describe('gameConfig', () => {
it('should return level 1 for score 0', () => {
expect(calculateLevel(0)).toBe(1);
});
it('should return 1 target for levels 1-3', () => {
expect(getTargetCountForLevel(1)).toBe(1);
expect(getTargetCountForLevel(3)).toBe(1);
});
});import type { ReactNode } from 'react';
import { vi } from 'vitest';
vi.mock('@react-three/fiber', () => ({
Canvas: ({ children }: { children: ReactNode }) => <div>{children}</div>,
useFrame: vi.fn(),
useThree: () => ({ camera: {}, scene: {}, gl: {} }),
}));
vi.mock('@react-three/drei', () => ({
Sparkles: ({ children }: { children?: ReactNode }) => <>{children}</>,
Trail: ({ children }: { children?: ReactNode }) => <>{children}</>,
}));import { vi, afterEach } from 'vitest';
beforeEach(() => {
const seq = [0.1, 0.2, 0.3];
let i = 0;
vi.spyOn(Math, 'random').mockImplementation(() => seq[i++ % seq.length]);
});
afterEach(() => { vi.restoreAllMocks(); });it('should reject non-finite scores', () => {
expect(() => saveHighScore(Number.NaN)).toThrow(RangeError);
expect(() => saveHighScore(Infinity)).toThrow(RangeError);
expect(() => saveHighScore(-1)).toThrow(RangeError);
});// BAD: testing implementation
expect(component.state.internalValue).toBe(5);
// BAD: non-deterministic
expect(result).toBe(Math.random());
// BAD: vague test name
it('works', () => { /* … */ });
// BAD: shared state between tests
let counter = 0;
it('t1', () => { counter++; });
it('t2', () => { expect(counter).toBe(1); }); // order-dependent!
// BAD: production data
const user = { email: 'real.user@company.com' }; // PII in tests- Reproduce locally with the same seed/time
- Classify: timing / network / shared-state / order-dependence
- Fix at the root (fake timers / explicit mocks / cleanup); never
retryas a workaround - Add a regression test for the fix
- ≥ 80 % line / ≥ 70 % branch coverage on changed modules
- ≥ 95 % coverage on security-sensitive code
- Deterministic (no real clocks, RNG, network)
- Three.js mocked in unit tests; logic tested in
src/utils/ - E2E covers at least one critical user flow for the feature
- No production / PII data in fixtures
- CI green locally (
npm run test:ci,npm run test:e2e:ci)