Testing Standards and Process
Test Quality Standards
Clean Test Output
- Encourage meaningful test output: Logs that provide insight into test behavior are valuable
- Suppress framework noise: React warnings, deprecation notices, and other framework chatter should be suppressed in test output
- Focus on signal over noise: Test output should clearly show what’s being tested and any failures
Test Layer Strategy
Unit Tests
- Mock all dependencies: External APIs, databases, file systems, and other services must be mocked
- Test in isolation: Each unit should be tested independently of its dependencies
- Fast execution: Unit tests should run quickly without network calls or I/O operations
- High coverage: Aim for comprehensive coverage of business logic and edge cases
Integration Tests
- Test component interaction: Verify that frontend and backend components work together correctly
- Real component integration: Use actual components but mock external services
- API contract validation: Ensure frontend and backend agree on data formats and endpoints
- Limited scope: Focus on critical integration points, not full system flows
Acceptance Tests
- Minimal but critical: Keep acceptance tests to a small, focused set that validates core user journeys & requirements
- Real system validation: Test against actual deployed systems
- User perspective: Test from the user’s point of view, not internal implementation details
- Success gate: Acceptance tests are the final validation before considering work complete
- Use real test instances: When a test instance of an external service exists (e.g., HubSpot sandbox), acceptance tests MUST use it instead of mocking. Mocking hides integration failures that only surface in production.
- Never mock what you can test for real: Only mock external services at the acceptance layer when no test instance is available and creating one is infeasible
Success Criteria
Definition of Done
- Unit tests pass: All business logic is validated in isolation
- Integration tests pass: Component interactions work correctly
- Acceptance tests pass: Critical user flows work end-to-end
- Only then celebrate success: Do not consider work complete until all test layers pass
Test Execution Order
- Run unit tests first (fastest feedback)
- Run integration tests second (moderate feedback)
- Run acceptance tests last (comprehensive validation)
- All layers must pass before deployment or completion
Implementation Guidelines
Test Organization
- Co-locate unit tests with source code using
.test.tssuffix - Place integration tests in dedicated
integration/directories - Keep acceptance tests separate directories, usually
tests/acceptance/ - Use descriptive test names that explain the behavior being tested
Mocking Strategy
- Mock external dependencies at the boundary (APIs, databases, file systems)
- Use consistent mocking patterns across the codebase
- Prefer dependency injection to enable easier mocking
- Document mock behavior and assumptions
Test Data Management
- Use factories or builders for test data creation
- Isolate test data to prevent cross-test contamination
- Clean up test data after each test run
- Use realistic but anonymized data for testing
Quality Gates
Before Code Review
- All unit tests must pass
- New code must have corresponding unit tests
- Integration tests for modified components must pass
Before Deployment
- Complete test suite must pass (unit + integration + acceptance)
- No test warnings or errors in output
- Performance tests (if applicable) must meet thresholds
Continuous Integration
- Tests run automatically on every commit
- Failed tests block merging
- Test results are clearly reported and actionable
Anti-Patterns to Avoid
- Never use
test.skip()or conditional skips: Tests MUST fail loudly when required credentials or environment variables are missing — both in CI (GitHub Actions must surface the failure) and locally (Node process must exit non-zero). Silent skips hide misconfiguration and create false confidence. If a test requires a credential, assert its presence at the top of the test or describe block and throw if absent. - Don’t mock services that have test instances: If we have a test/sandbox account for a service (e.g., HubSpot test portal), use it. Mocking hides real integration failures.
- Don’t skip acceptance validation: Unit and integration tests alone are insufficient
- Don’t ignore test warnings: Clean up noisy test output
- Don’t mock everything in integration tests: Some real components should interact
- Don’t write too many acceptance tests: Keep them focused and maintainable
- Don’t celebrate before acceptance passes: Premature success declarations lead to production issues
- Don’t invent new test mechanisms or setups unless confirmed: The test structure is deliberate, if you think new things are needed, cover it in the design.md & seek confirmation before continuing.
CI Environment Variables
Use canonical names for CI secrets and variables. Do not invent new names.
| Name | Type | Purpose |
|---|---|---|
CLERK_SECRET_KEY | secret | Clerk backend API key for E2E auth flows |
VITE_CLERK_PUBLISHABLE_KEY | secret | Clerk frontend publishable key |
E2E_CLERK_EMAIL | variable (not secret) | Test user email for Clerk E2E sign-in |
HUBSPOT_PRIVATE_APP_TOKEN | secret | HubSpot private app token for API access |
When adding new CI variables, document them here. Use GitHub Actions secrets for sensitive values and vars for non-sensitive configuration.