AgentSkillsCN

testing

结合 testcontainers 与 Vitest 的共享数据库测试模式。适用于编写后端测试、搭建测试文件、调试测试失败问题,或配置 Vitest 测试环境时使用。该方案会在“write tests”、“test setup”、“testcontainers”、“vitest config”、“test isolation”等事件触发时自动运行,或在新建测试套件时自动启用。

SKILL.md
--- frontmatter
name: testing
description: Shared database testing patterns with testcontainers and Vitest. Use when writing backend tests, setting up test files, debugging test failures, or configuring Vitest. Triggers on "write tests", "test setup", "testcontainers", "vitest config", "test isolation", or when creating new test suites.

Testing

This repo uses a shared PostgreSQL container with TRUNCATE resets for fast, isolated tests.

Quick Start

typescript
import { describe, it, expect, beforeAll, beforeEach } from "vitest";
import type { Kysely } from "kysely";
import type { DB as DatabaseSchema } from "#schema";
import { getSharedDatabaseHelper, resetSharedDatabase, createTestUser } from "#test-helpers";

describe("My Test Suite", () => {
  let db: Kysely<DatabaseSchema>;

  beforeAll(async () => {
    const dbHelper = await getSharedDatabaseHelper();
    db = dbHelper.db;
  });

  beforeEach(async () => {
    await resetSharedDatabase();
  });

  it("should work", async () => {
    const user = await createTestUser(db);
    expect(user).toBeDefined();
  });
});

Key Functions

FunctionPurpose
getSharedDatabaseHelper()Get DB connection (call in beforeAll)
resetSharedDatabase()Reset to clean state (call in beforeEach)
createTestUser(db)Create test user fixture

Vitest Config Requirements

typescript
// vitest.config.ts
export default defineConfig({
  test: {
    pool: "threads",      // REQUIRED: threads, not forks
    maxConcurrency: 4,    // Limit parallel tests
    isolate: false,       // Reuse context for speed
    hookTimeout: 120000,  // 2 min for container startup
    testTimeout: 30000,   // 30 sec per test
  }
});

Use createSharedTestConfig() from @yourcompany/backend-core/vitest.config.shared:

typescript
import { mergeConfig, defineConfig } from "vitest/config";
import { createSharedTestConfig } from "@yourcompany/backend-core/vitest.config.shared";

export default mergeConfig(
  createSharedTestConfig({ setupFiles: ["./src/test-setup.ts"] }),
  defineConfig({ /* overrides */ })
);

Common Patterns

Shared Test Data

typescript
let testUser: User;

beforeEach(async () => {
  await resetSharedDatabase();
  testUser = await createTestUser(db); // Create AFTER reset
});

Nested Describes

typescript
describe("Feature", () => {
  beforeEach(async () => {
    await resetSharedDatabase(); // Applies to ALL nested tests
  });

  describe("sub-feature", () => {
    it("test 1", () => { /* clean DB */ });
    it("test 2", () => { /* clean DB */ });
  });
});

Troubleshooting

IssueCauseSolution
"Database not initialized"Test ran before setupAdd beforeAll with getSharedDatabaseHelper()
Tests interferingMissing resetAdd resetSharedDatabase() in beforeEach
Connection refusedTimeout too shortIncrease hookTimeout
Slow suiteCheck logsShould see only ONE container startup

Rules

  • Use pool: "threads" (not forks)
  • Always call resetSharedDatabase() in beforeEach
  • No afterAll cleanup needed - handled globally
  • Use DI in app code to enable test fakes

Storybook (UI Testing)

bash
pnpm --filter @yourcompany/web storybook

Ensure VITE_API_URL and VITE_AUTH_URL are set in .env.development.