# Testing Requirements Test-driven development standards and practices for Mosaic Stack. ## Testing Philosophy - **Test-Driven Development (TDD)** — Write tests before implementation - **Minimum 85% coverage** for all new code - **All tests must pass** before PR approval - **No untested code** in production ## Test Types ### Unit Tests Test individual functions and methods in isolation. **Location:** `*.spec.ts` next to source file **Example:** ```typescript // apps/api/src/auth/auth.service.spec.ts describe("AuthService", () => { it("should create a session for valid user", async () => { const result = await authService.createSession(mockUser); expect(result.session.token).toBeDefined(); }); }); ``` ### Integration Tests Test interactions between components. **Location:** `*.integration.spec.ts` in module directory **Example:** ```typescript // apps/api/src/auth/auth.integration.spec.ts describe("Auth Integration", () => { it("should complete full login flow", async () => { const login = await request(app.getHttpServer()) .post("/auth/sign-in") .send({ email, password }); expect(login.status).toBe(200); }); }); ``` ### E2E Tests Test complete user flows across the entire stack. **Location:** `apps/web/tests/e2e/` (when implemented) **Framework:** Playwright ## Running Tests ```bash # All tests pnpm test # Watch mode (re-run on changes) pnpm test:watch # Coverage report pnpm test:coverage # Specific file pnpm test apps/api/src/auth/auth.service.spec.ts # API tests only pnpm test:api # E2E tests (when implemented) pnpm test:e2e ``` ## Writing Tests ### Structure ```typescript import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; describe("ComponentName", () => { beforeEach(() => { // Setup }); afterEach(() => { // Cleanup }); describe("methodName", () => { it("should handle normal case", () => { // Arrange const input = "test"; // Act const result = component.method(input); // Assert expect(result).toBe("expected"); }); it("should handle error case", () => { expect(() => component.method(null)).toThrow(); }); }); }); ``` ### Mocking ```typescript // Mock service dependency const mockPrismaService = { user: { findUnique: vi.fn(), create: vi.fn(), }, }; // Mock module vi.mock("./some-module", () => ({ someFunction: vi.fn(() => "mocked"), })); ``` ### Testing Async Code ```typescript it("should complete async operation", async () => { const result = await asyncFunction(); expect(result).toBeDefined(); }); // Or with resolves/rejects it("should resolve with data", async () => { await expect(asyncFunction()).resolves.toBe("data"); }); it("should reject with error", async () => { await expect(failingFunction()).rejects.toThrow("Error"); }); ``` ## Coverage Requirements ### Minimum Coverage - **Statements:** 85% - **Branches:** 80% - **Functions:** 85% - **Lines:** 85% ### View Coverage Report ```bash pnpm test:coverage # Opens HTML report open coverage/index.html ``` ### Exemptions Some code types may have lower coverage requirements: - **DTOs/Interfaces:** No coverage required (type checking sufficient) - **Constants:** No coverage required - **Database migrations:** Manual verification acceptable Always document exemptions in PR description. ## Best Practices ### 1. Test Behavior, Not Implementation **❌ Bad:** ```typescript it("should call getUserById", () => { service.login(email, password); expect(mockService.getUserById).toHaveBeenCalled(); }); ``` **✅ Good:** ```typescript it("should return session for valid credentials", async () => { const result = await service.login(email, password); expect(result.session.token).toBeDefined(); expect(result.user.email).toBe(email); }); ``` ### 2. Use Descriptive Test Names **❌ Bad:** ```typescript it('works', () => { ... }); it('test 1', () => { ... }); ``` **✅ Good:** ```typescript it('should return 401 for invalid credentials', () => { ... }); it('should create session with 24h expiration', () => { ... }); ``` ### 3. Arrange-Act-Assert Pattern ```typescript it("should calculate total correctly", () => { // Arrange - Set up test data const items = [{ price: 10 }, { price: 20 }]; // Act - Execute the code being tested const total = calculateTotal(items); // Assert - Verify the result expect(total).toBe(30); }); ``` ### 4. Test Edge Cases ```typescript describe("validateEmail", () => { it("should accept valid email", () => { expect(validateEmail("user@example.com")).toBe(true); }); it("should reject empty string", () => { expect(validateEmail("")).toBe(false); }); it("should reject null", () => { expect(validateEmail(null)).toBe(false); }); it("should reject invalid format", () => { expect(validateEmail("notanemail")).toBe(false); }); }); ``` ### 5. Keep Tests Independent ```typescript // ❌ Bad - Tests depend on order let userId; it("should create user", () => { userId = createUser(); }); it("should get user", () => { getUser(userId); // Fails if previous test fails }); // ✅ Good - Each test is independent it("should create user", () => { const userId = createUser(); expect(userId).toBeDefined(); }); it("should get user", () => { const userId = createUser(); // Create fresh data const user = getUser(userId); expect(user).toBeDefined(); }); ``` ## CI/CD Integration Tests run automatically on: - Every push to feature branch - Every pull request - Before merge to `develop` **Pipeline must pass** before merging. ## Debugging Tests ### Run Single Test ```typescript it.only("should test specific case", () => { // Only this test runs }); ``` ### Skip Test ```typescript it.skip("should test something", () => { // This test is skipped }); ``` ### Verbose Output ```bash pnpm test --reporter=verbose ``` ### Debug in VS Code Add to `.vscode/launch.json`: ```json { "type": "node", "request": "launch", "name": "Vitest", "runtimeExecutable": "pnpm", "runtimeArgs": ["test", "--no-coverage"], "console": "integratedTerminal" } ``` ## Test Database Use separate test database: ```bash # .env.test TEST_DATABASE_URL=postgresql://mosaic:mosaic@localhost:5432/mosaic_test # Reset test DB before each run pnpm test:db:reset ``` ## Next Steps - **Review Commit Guidelines** — [Committing](3-committing.md) - **Learn Database Testing** — [Database](../2-database/3-prisma.md) - **Check Code Style** — [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html)