Context
The backend currently has no automated integration tests. Testing is done manually via curl against a shared Supabase dev database with seeded mock data. This works for development but doesn't scale — mock data changes between sessions, tests aren't reproducible, and there's no CI verification.
Recommended Enterprise Testing Strategy
Three-Layer Test Pyramid
Layer 1: Unit Tests (fast, no DB)
- What: Test business logic in services and utilities
- How: Mock repositories with
mocktail, use Freezed models as test data
- Where:
backend/test/unit/
- When: Every commit, pre-push hooks
- Coverage target: All service methods, validation logic, auth flows
// Example: test auth service
test('signUpWithEmail creates user + profile + account', () async {
when(() => mockDb.findUserByEmail(any())).thenAnswer((_) async => null);
when(() => mockDb.createUser(...)).thenAnswer((_) async => testUser);
final result = await authService.signUpWithEmail('test@example.com', 'pass');
expect(result['user']['email'], 'test@example.com');
verify(() => mockDb.createConsulteeProfile(...)).called(1);
});
Layer 2: Integration Tests (with DB)
- What: Test full request→DB→response flow
- How: Start dart_frog server, hit endpoints with HTTP client, verify JSON responses
- Where:
backend/test/integration/
- DB: Dedicated test database (NOT the shared dev Supabase)
- When: PR checks, nightly builds
// Example: integration test
test('POST /api/feedback creates feedback with auto-generated UUID', () async {
final response = await http.post(
Uri.parse('http://localhost:8080/api/feedback'),
headers: {'Authorization': 'Bearer $testToken', 'Content-Type': 'application/json'},
body: jsonEncode({'title': 'Test', 'description': 'Integration test', 'rating': 5}),
);
expect(response.statusCode, 201);
final body = jsonDecode(response.body);
expect(body['id'], isNotEmpty); // UUID auto-generated
expect(body['status'], 'PENDING'); // Enum correct case
expect(DateTime.tryParse(body['createdAt']), isNotNull); // DateTime valid
});
Layer 3: E2E Tests (full stack)
- What: Flutter app → API → DB → response → UI verification
- How: Flutter integration_test package
- Where:
test/integration/
- When: Before releases
Database Environments
| Environment |
Database |
Data |
Purpose |
| Test (CI) |
Docker PostgreSQL per run |
Deterministic seed |
Automated tests — reproducible, isolated |
| Dev |
Supabase dev project |
Seeded mock data |
Manual development and debugging |
| Staging |
Supabase staging project |
Sanitized prod copy |
Pre-release verification |
| Production |
Supabase prod project |
Real user data |
Live |
Deterministic Test Seeding
Create backend/test/fixtures/seed.dart:
/// Deterministic test data — same IDs every run
class TestFixtures {
static const consultantUserId = 'test-consultant-001';
static const consulteeUserId = 'test-consultee-001';
static const domainId = 'test-domain-001';
static const planId = 'test-plan-001';
static Future<void> seed(DatabaseClient db) async {
await db.executeRaw('DELETE FROM "Feedback" WHERE "userId" LIKE \'test-%\'');
// ... seed known test records with fixed IDs
}
static Future<void> teardown(DatabaseClient db) async {
await db.executeRaw('DELETE FROM "Feedback" WHERE "userId" LIKE \'test-%\'');
}
}
CI Pipeline Integration
# .github/workflows/backend-tests.yml
name: Backend Tests
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- run: cd backend && dart pub get
- run: cd backend && dart test test/unit/
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: test_familiarise
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- run: cd backend && dart pub get
- run: npx prisma migrate deploy # Apply schema
- run: dart run test/fixtures/seed.dart # Seed test data
- run: dart run build_runner build --delete-conflicting-outputs
- run: dart pub global run dart_frog_cli:dart_frog build
- run: dart build/bin/server.dart &
- run: sleep 5 && dart test test/integration/
Existing Test Infrastructure
The project already has:
backend/tool/gen_test_token.dart — JWT generation for test users
backend/tool/gen_token_for.dart — JWT for specific user IDs
backend/test/repositories/ — Some unit tests with mocks
- Supabase dev DB with seeded data (test_intg_cbj_cnt, test_intg_cbj_cte users)
Implementation Priority
- Phase 1: Automate the manual curl tests as integration tests (we already run 16 curl tests manually — convert to
dart test)
- Phase 2: Add unit tests for auth service, payment logic, booking conflict detection
- Phase 3: Docker PostgreSQL for CI — isolated, reproducible
- Phase 4: E2E Flutter integration tests
Key Principles
- Never test against production — always use test/dev environments
- Deterministic data — fixed IDs, predictable state, no random data in assertions
- Isolated per run — each CI run gets its own DB, seeds fresh, tears down after
- Fast feedback — unit tests run in seconds, integration tests in under a minute
Context
The backend currently has no automated integration tests. Testing is done manually via curl against a shared Supabase dev database with seeded mock data. This works for development but doesn't scale — mock data changes between sessions, tests aren't reproducible, and there's no CI verification.
Recommended Enterprise Testing Strategy
Three-Layer Test Pyramid
Layer 1: Unit Tests (fast, no DB)
mocktail, use Freezed models as test databackend/test/unit/Layer 2: Integration Tests (with DB)
backend/test/integration/Layer 3: E2E Tests (full stack)
test/integration/Database Environments
Deterministic Test Seeding
Create
backend/test/fixtures/seed.dart:CI Pipeline Integration
Existing Test Infrastructure
The project already has:
backend/tool/gen_test_token.dart— JWT generation for test usersbackend/tool/gen_token_for.dart— JWT for specific user IDsbackend/test/repositories/— Some unit tests with mocksImplementation Priority
dart test)Key Principles