6.3 KiB
Testing Patterns
Analysis Date: 2026-04-30
Test Framework
Runner:
- Vitest 4.x for unit/integration tests in both packages:
- Frontend config:
frontend/vitest.config.ts - Backend config:
backend/vitest.config.ts
- Frontend config:
- Config evidence:
- Frontend uses
environment: 'jsdom'with React setup filefrontend/src/test/setup.ts. - Backend uses
environment: 'node'with setup filebackend/src/test/setup.ts.
- Frontend uses
Assertion Library:
- Vitest
expect. - Frontend extends DOM assertions via
@testing-library/jest-dominfrontend/src/test/setup.ts.
Run Commands:
cd frontend && npm test # Watch/unit tests
cd frontend && npm run test:run # CI-style frontend run
cd frontend && npm run test:coverage # Frontend coverage
cd backend && npm test # Watch/unit tests
cd backend && npm run test:run # CI-style backend run
cd backend && npm run test:coverage # Backend coverage
cd frontend && npm run test:e2e # Stable Playwright suite
cd frontend && npm run test:e2e:all # Cross-browser Playwright suite
Test File Organization
Location:
- Backend unit/integration tests are in
backend/src/test/*.test.ts. - Frontend unit/component/hook/context tests are in
frontend/src/test/**. - Browser E2E tests are in
frontend/e2e/*.spec.ts.
Naming:
- Unit/integration:
*.test.tsor*.test.tsx(for examplebackend/src/test/routes-real.test.ts,frontend/src/test/components/MedicationDialogs.test.tsx). - E2E:
*.spec.ts(for examplefrontend/e2e/medication-edit.spec.ts).
Structure:
backend/src/test/
setup.ts
*.test.ts
frontend/src/test/
setup.ts
App.test.tsx
components/*.test.tsx
context/*.test.tsx
hooks/*.test.ts
pages/*.test.tsx
utils/*.test.ts
frontend/e2e/
auth.setup.ts
fixtures/index.ts
*.spec.ts
Test Structure
Suite Organization:
describe("Feature Area", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("handles expected behavior", async () => {
// arrange
// act
// assert
expect(result).toEqual(expected);
});
});
Pattern evidence: frontend/src/test/components/MobileEditModal.test.tsx, backend/src/test/planner.test.ts.
Patterns:
- Setup pattern:
- Frontend centralizes browser mocks in
frontend/src/test/setup.ts(fetch, localStorage, clipboard, history, i18n). - Backend provides reusable app/database factories in
backend/src/test/setup.ts(buildTestApp,createTestUser,createTestMedication).
- Frontend centralizes browser mocks in
- Teardown pattern:
afterAllcloses Fastify app and DB clients (backend/src/test/planner.test.ts,backend/src/test/integration.test.ts).
- Assertion pattern:
- Route tests assert both HTTP status and response body (
backend/src/test/routes-real.test.ts). - UI tests assert presence and behavior via Testing Library role/test-id queries (
frontend/src/test/components/MedicationDialogs.test.tsx).
- Route tests assert both HTTP status and response body (
Mocking
Framework:
- Vitest mocks (
vi.mock,vi.fn,vi.hoisted,vi.stubGlobal).
Patterns:
const { testClient, testDb } = vi.hoisted(() => {
const client = createClient({ url: ":memory:" });
const db = drizzle(client);
return { testClient: client, testDb: db };
});
vi.mock("../db/client.js", () => ({
db: testDb,
migrationsReady: Promise.resolve(),
}));
Pattern evidence: backend/src/test/integration.test.ts, backend/src/test/routes-real.test.ts.
vi.mock("../../components/ConfirmModal", () => ({
ConfirmModal: ({ onConfirm }) => <button onClick={onConfirm}>confirm</button>,
}));
Pattern evidence: frontend/src/test/components/MedicationDialogs.test.tsx.
What to Mock:
- External side effects and infrastructure boundaries: SMTP/nodemailer, fetch network calls, auth/plugin env modules, browser APIs.
- Component dependencies in focused unit tests (replace heavy children with stubs).
What NOT to Mock:
- Core business behavior under direct test (route handlers in route tests, hook logic in hook tests, E2E API + UI flow in Playwright).
Fixtures and Factories
Test Data:
const userId = await createTestUser(client, { username: "testuser" });
const medId = await createTestMedication(client, { userId, name: "Test Medication" });
Pattern evidence: backend/src/test/setup.ts, used by backend/src/test/medications.test.ts.
export const test = base.extend({
page: async ({ page }, use) => {
await applyVideoSafetyMode(page);
await setupAuthMeMock(page);
await use(page);
},
});
Pattern evidence: frontend/e2e/fixtures/index.ts.
Location:
- Backend factories/utilities:
backend/src/test/setup.ts. - Frontend E2E shared fixtures and API helpers:
frontend/e2e/fixtures/index.ts.
Coverage
Requirements:
- Frontend global thresholds in
frontend/vitest.config.ts: lines/functions/branches/statements = 75. - Backend global thresholds in
backend/vitest.config.ts: lines 60, functions 65, branches 50, statements 60.
View Coverage:
cd frontend && npm run test:coverage
cd backend && npm run test:coverage
Test Types
Unit Tests:
- Component/hook/utils tests in
frontend/src/test/**. - Utility/service route-unit style tests in
backend/src/test/*.test.ts.
Integration Tests:
- Backend route interaction and multi-route behavior tests in files like:
backend/src/test/integration.test.tsbackend/src/test/routes-real.test.ts
E2E Tests:
- Playwright used with setup project and browser projects (
frontend/playwright.base.config.ts). - Auth/session and API seeding helpers in
frontend/e2e/fixtures/index.ts.
Common Patterns
Async Testing:
await waitFor(() => {
expect(mockFn).toHaveBeenCalledTimes(1);
});
Pattern evidence: frontend/src/test/context/AppContext.test.tsx.
const response = await app.inject({ method: "GET", url: "/settings" });
expect(response.statusCode).toBe(200);
Pattern evidence: backend/src/test/routes-real.test.ts.
Error Testing:
const response = await app.inject({ method: "POST", url: "/planner/send-email", payload: { rows: [] } });
expect(response.statusCode).toBe(400);
expect(response.json()).toEqual({ error: "Missing planner data" });
Pattern evidence: backend/src/test/planner.test.ts.
Testing analysis: 2026-04-30