Testing Strategy
Citadel employs a multi-layered testing strategy to ensure reliability across its microservices architecture, frontend applications, and critical user flows.
1. Unit Testing
Unit tests focus on individual functions, classes, and modules in isolation.
Backend Services (Go)
Each Go service includes a helper script scripts/test.sh that handles unit tests, integration tests, and coverage reporting.
- Command:
./scripts/test.sh(within a service directory) - What it does: Runs
go testwith race detection, coverage profiles, and appropriate flags.
To run tests for a specific package from the root using Turbo:
# Test a specific package (e.g., iam-service)
pnpm -F iam-service test
# Lint a specific package
pnpm -F iam-service lint
Frontend Applications (React)
- Framework: Jest / Vitest + React Testing Library.
- Command:
pnpm -F <app-name> test(e.g.,pnpm -F admin-portal-ui test).
2. End-to-End (E2E) Testing
End-to-End tests verify the integrated system from the user's perspective. We use Playwright to simulate user interactions against a running environment.
Location
All E2E tests are located in scripts/tests:
scripts/tests/ui/: UI-driven flows (login, registration).scripts/tests/api/: Direct API calls to verify backend logic.
Running E2E Tests
The scripts/e2e.sh script is the single entry point for running the test suite. It handles:
- Database Reset: Cleans the database to a known state.
- Service Startup: Boots up all services (IAM, Keycloak, Frontend, etc.).
- Test Execution: Runs Playwright.
Prerequisites
- Docker & Docker Compose running.
- Node.js installed (
pnpm install). - Playwright browsers installed:
pnpm exec playwright install --with-deps.
Command
./scripts/e2e.sh
Interactive Mode (UI)
To debug tests or watch them execute:
./scripts/e2e.sh --ui
This opens the Playwright Inspector, allowing you to step through tests and view traces.
Writing New E2E Tests
- Create a new
.spec.tsfile inscripts/tests/uiorscripts/tests/api. - Use the
testobject from@playwright/test. - Leverage helper functions in
scripts/tests/utilsfor common tasks like generating random users or tokens.
import { test, expect } from '@playwright/test';
test('should allow user to login', async ({ page }) => {
await page.goto('http://localhost:5000');
await page.getByRole('button', { name: 'Login' }).click();
// ... interactions ...
await expect(page).toHaveURL(/dashboard/);
});