コンテンツにスキップ

テスト

AdaptiveLearnerのテスト規律は、すべての変更に対してmake testによって強制されます。戦略はピラミッド形式です。ベースにユニットテスト、中間に統合テスト、頂点にE2Eスモークテストが位置します。

テスト数(v1.20.0)

レイヤー 件数 ツール
バックエンドユニット + 統合 786 pytest ^9
プラグインテスト(10プラグイン) 615 pytest ^9
フロントエンドユニット + 統合 1233 Vitest 4
E2Eスモーク 16スペックファイル Playwright
合計(make test 2634

プラグイン内訳: assessment 110 + ai-anthropic 34 + ai-openai 31 + ai-gemini 33 + session 215 + tracking 64 + tools 58 + gamification 23 + anki 20 + notebooklm 27。

バックエンドpytest

make test-backend      # 786テスト、約35秒
cd backend && poetry run pytest -k "test_session" -v
cd backend && poetry run pytest --pdb

テストはbackend/tests/に格納されています。conftest.pyのフィクスチャが、テストごとにフレッシュなインメモリSQLite DB、TestClient、モック化されたプラグインマネージャーを提供します。テスト分離は厳格で、app.*のインポート前にADAPTIVE_LEARNER_TEST=1が設定されます。

プラグインテスト

各プラグインは独自のtests/ディレクトリを持ちます。

make test-plugins              # すべて
make test-plugin-session       # 1つだけ
cd plugins/adaptive-learner-plugin-session && poetry run pytest

プラグインテストはFastAPIアプリをロードしません。プラグインのモジュールを単独でテストします。フック発火をテストする際はpluggy.PluginManagerをモックしてください。

フロントエンドVitest

make test-frontend                # 387テスト、約2秒
cd frontend && npx vitest         # watchモード
cd frontend && npx vitest run src/storage/  # 1つのディレクトリ

テストはソースの隣に配置されます: Component.tsxの隣にComponent.test.tsx。環境はhappy-dom; React 19 + RTL。

モックパターン

AIプロバイダー: global.fetchをモックし、URL、ヘッダー、ボディをアサートします。

beforeEach(() => {
  global.fetch = vi.fn(async (input, init) => {
    calls.push({url, method, body});
    return new Response(JSON.stringify({content: [{type: "text", text: "hi"}]}), {status: 200});
  });
});

fake-indexeddb: すべてのDexieテストファイルの先頭に記述します。

import "fake-indexeddb/auto";

beforeEach(async () => {
  await _resetDbForTests();
  const {IDBFactory} = await import("fake-indexeddb");
  (globalThis as unknown as {indexedDB: IDBFactory}).indexedDB = new IDBFactory();
});

各テストはフレッシュなインメモリIndexedDBを取得します。リークはありません。

api/client.tsのモック(レガシーページ):

vi.mock("../api/client", async () => {
  const actual = await vi.importActual<typeof import("../api/client")>("../api/client");
  return {...actual, api: {...actual.api, users: {...actual.api.users, get: apiGetMock}}};
});

ページはgetStorage()をインポートし、それがApiStorageに委譲し、さらにapi.*に委譲します。モックはapi.*レイヤーで割り込み、ストレージスタックを通じて発火します。

Playwright E2E

cd e2e && npx playwright test
cd e2e && npx playwright test --ui   # インタラクティブ
cd e2e && npx playwright test smoke/mobile-viewports.spec.ts

スモークスペックは重要なユーザーパスをカバーしています。

  • ランディングの言語ピッカー + オンボーディングフォーム
  • アセスメント12問 + レーダーレンダリング
  • セッション開始 + 終了 + レーティング
  • Settings言語 + APIキー
  • カリキュラム作成
  • モバイルビューポート(iPhone SE、iPhone 14、Pixel 7、iPad)

スペックはdata-testidセレクターのみを使用します。壊れやすいCSSセレクターは使用しません。スモークスペックはmake testのパスには含まれていません。実行中のアプリが必要です(先にmake dev-bgを実行)。

カバレッジ

make test-coverage   # オプション; 低速 + 熱負荷が高い

カバレッジはmainへのすべてのプッシュについてCIで実行されます。アーティファクトをダウンロードするには:

gh run download --name backend-coverage
gh run download --name frontend-coverage

.claude/rules/quality-checks.mdのターゲット:

  • サービス + ビジネスロジック: 最低95%
  • APIエンドポイント: 最低90%
  • ロジックを持つフロントエンドコンポーネント: 最低85%
  • フック + ユーティリティ: 最低95%

全体: プロジェクト全体で85〜95%。

pre-commit

cd backend && poetry run pre-commit install

フック: ruff check(自動修正)、ruff format、末尾の空白、ファイル末尾の修正、check-yaml、check-merge-conflict。バックエンドのみ。フロントエンドのリントはpre-commitではなく、CI時に実行されます。

CI

.github/workflows/ci.ymlはmainへのすべてのプッシュとすべてのPRで実行されます。

  1. バックエンドテスト(Python 3.12 + 3.13マトリクス)
  2. プラグインテスト(プラグインごとに1ジョブ; マトリクス戦略)
  3. フロントエンドVitest + tsc + lint
  4. ruff check + format-check

.github/workflows/release-gate.ymlはタグプッシュ時に実行されます: バージョンピンが同期されていること(12ファイル全体でドリフトなし)、プラグインのロックファイルが一致すること、再生成されたアーティファクトが最新であることを検証します。