Testing Strategy Skill
This skill provides detailed implementation guidance for testing. The core mandates
(TDD, AAA, test pyramid ratios) are in the testing-strategy rule and are always active.
Load this skill when you need specific patterns, tools, or language conventions.
Test Doubles Strategy
Unit Tests: Use mocks/stubs for all driven ports
- Mock repositories return pre-defined data
- Mock external APIs return successful responses
- Mock time/random for deterministic tests
- Control test environment completely
Integration Tests: Use real infrastructure
- Testcontainers spins up PostgreSQL, Redis, message queues
- Firebase emulator spins up Firebase Authentication, Cloud Firestore, Realtime Database, Cloud Storage for Firebase, Firebase Hosting, Cloud Functions, Pub/Sub, and Firebase Extensions
- Test actual database queries, connection handling, transactions
- Verify adapter implementations work with real services
Best Practice: Generate at least 2 implementations per driven port:
- Production adapter (PostgreSQL, GCP GCS, etc.)
- Test adapter (in-memory, fake implementation)
Test Organization
Co-located Tests (Unit & Integration)
Place tests next to the file they test. This keeps tests visible, encourages maintenance, and supports refactoring.
Naming Conventions by Language:
| Language | Unit | Integration |
|---|---|---|
| TS/JS | framework-dependent — see note below | framework-dependent |
| Go | *_test.go |
*_integration_test.go |
| Dart/Flutter | *_test.dart (in test/ mirroring lib/) |
*_integration_test.dart |
| Python | test_*.py |
test_*_integration.py |
| Java | *Test.java |
*IT.java |
| Rust | #[cfg(test)] mod tests inline |
tests/ at crate root |
TS/JS test-naming is framework-dependent, not a single suffix. Authoritative rule:
@.agents/skills/typescript-idioms/references/project-structure.md§Test Organization. Summary: Vue & Angular use*.spec.ts(CLI default); React uses*.test.tsx; generic TS, Hono, and Next.js App Router use*.test.ts(Next.js co-locates in__tests__/). Do not mixtestandspecsuffixes within a single package — follow that package's framework convention.
Language-Specific Overrides
Flutter: Tests live in test/ mirroring lib/ layout (Flutter's default convention, discovered by flutter test). See the flutter-idioms skill's project-structure reference.
Rust: Unit tests are inline (#[cfg(test)] mod tests at the bottom of each .rs file). This is the official Rust convention — it enables testing private functions and is how cargo test discovers unit tests. Integration tests go in tests/ at the crate root, compiled as separate crates. See the rust-idioms skill.
E2E Tests (Separate)
Place in a dedicated e2e/ folder:
- Single app:
e2e/at project root - Monorepo:
apps/e2e/subdivided by scope:apps/e2e/api/andapps/e2e/ui/ - Naming:
{feature}-{ui/api}.e2e.test.{ext}
For interactive E2E testing, see the browser-automation skill.
Test Quality Standards
AAA Pattern Example:
// Arrange: Set up test data and mocks
const user = { id: '123', email: 'test@example.com' };
const mockRepo = createMockRepository();
// Act: Execute the code under test
const result = await userService.createUser(user);
// Assert: Verify expected outcome
expect(result.id).toBe('123');
expect(mockRepo.save).toHaveBeenCalledWith(user);
Test Naming: should [expected behavior] when [condition]
should return 404 when user not foundshould hash password before saving to databaseshould reject email with invalid format
Coverage Requirements:
- Unit tests: >85% code coverage
- Integration tests: All adapter implementations
- E2E tests: Critical user journeys
Test doubles co-location: Place mocks next to interface (e.g., storage_mock.go, taskAPI.mock.ts)