Development
Testing
Unit testing with Vitest, shared test helpers, mocks, and coverage targets
Overview
The monorepo uses Vitest for unit testing with shared configuration and helpers from the @hn-monorepo/testing package. E2E testing is covered separately in the E2E Testing guide.
Quick Reference
| Command | Description |
|---|---|
bun test | Run all unit tests |
bun test:watch | Run tests in watch mode |
bun test:coverage | Run tests with coverage report |
turbo test --filter=<app> | Run tests for a specific app |
bun test:e2e | Run all E2E tests (Playwright) |
Shared Testing Package
The @hn-monorepo/testing package (packages/testing/) provides:
- Shared Vitest configuration
- Test fixtures and helpers
- Mock factories for common dependencies
- Playwright configuration factory (for E2E)
Shared Mocks
Pre-built mocks are available for commonly used dependencies:
| Mock | Purpose |
|---|---|
| Next.js mocks | next/navigation, next/image, next/headers |
| Convex mocks | convex/react hooks, query/mutation wrappers |
| i18n mocks | next-intl translation functions |
Import mocks from the testing package:
import { mockConvex, mockNavigation, mockI18n } from "@hn-monorepo/testing";
Writing Unit Tests
Test File Location
Unit tests live alongside the code they test or in a tests/ directory:
apps/<app>/
├── src/
│ └── components/
│ └── feature/
│ ├── feature.tsx
│ └── feature.test.ts # Co-located test
├── tests/ # Or in a tests directory
│ └── feature.test.ts
Basic Test Structure
import { describe, expect, it } from "vitest";
describe("FeatureName", () => {
it("should handle the expected case", () => {
const result = myFunction(input);
expect(result).toBe(expected);
});
});
Testing Guidelines
When to Write Tests
| Change | Test Requirement |
|---|---|
| Bug fix | Regression test required |
| New user flow | E2E test recommended |
| Convex business logic | Unit test recommended |
| Shared package changes | Unit test required |
| UI component | Test if complex logic involved |
Coverage Targets
- Shared packages (
packages/): Target 60%+ coverage - App code (
apps/): Coverage not enforced, but tests recommended for business logic - Convex functions: Unit tests recommended for business logic in
convex/model/layer
Convex Business Logic
Keep public API functions thin and delegate to a model layer. Test the model layer:
// convex/model/vocabulary.ts — business logic (testable)
export function validateVocabularySet(data: VocabularySetInput) {
// ...
}
// convex/functions/vocabulary.ts — thin API layer
export const create = mutation({
handler: async (ctx, args) => {
const validated = validateVocabularySet(args);
return ctx.db.insert("vocabularySets", validated);
},
});
Page Object Model
For complex E2E flows, use the page object model pattern with selectors defined in e2e/selectors/:
// e2e/selectors/login.ts
export const loginSelectors = {
emailInput: '[data-testid="email-input"]',
passwordInput: '[data-testid="password-input"]',
submitButton: '[data-testid="login-submit"]',
};
CI Integration
Unit tests run automatically in the CI pipeline:
- Job:
testin.github/workflows/ci.yml - Timeout: 15 minutes
- Trigger: Runs on all PRs and pushes to master
- Scope: Tests run for changed packages only (detected via git diff)
Test failures block PR merge.