mcpField
L
langchain4j-testing-strategies

langchain4j-testing-strategies

github.com/giuseppe-trisciuoglioテスト・品質3653.7k 回インストール7 週間前に更新
リポジトリ

langchain4j-testing-strategies について

LangChain4j を使う Java アプリケーション開発者向けのテスト戦略スキルです。AI Services、RAG ワークフロー、ツール実行を対象に、モデルのモック、Testcontainers による統合テスト、エンドツーエンド検証を扱います。

主な機能

  1. 01モデルのモックを使った単体テスト
  2. 02Testcontainers による実サービスのテスト
  3. 03RAG ワークフローとツール実行の検証
  4. 04ストリーミング、メモリ、エラー処理のテスト
  5. 05依存関係設定とテスト手順の参照

活用シーン

  1. 01LangChain4j AI Services の分離テスト
  2. 02コンテナ化モデル統合の検証
  3. 03RAG 検索チェーンのテスト
  4. 04ツール実行時のエラー処理確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill langchain4j-testing-strategies

インストール後、Claude Code で/langchain4j-testing-strategiesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
advanced-testing.md
integration-testing.md
testing-dependencies.md
unit-testing.md
workflow-patterns.md

詳細

LangChain4J のテスト戦略

概要

モックを使った単体テスト、Testcontainers を使った統合テスト、RAG システム、AI Services、ツール実行のエンドツーエンド検証のパターンです。

使用する場面

  • AI サービスの単体テスト:LangChain4j AiServices を使用するサービスを、すばやく分離してテストする必要がある場合
  • LangChain4j コンポーネントの統合テスト:Testcontainers を使用して実際の ChatModel、EmbeddingModel、RAG パイプラインをテストする場合
  • AI モデルのモック化:外部 API を呼び出さずに、決定論的な応答を得る必要がある場合
  • LLM ベースの Java アプリケーションのテスト:RAG ワークフロー、ツール実行、検索チェーンを検証する場合

手順

1. モックを使った単体テスト

すばやく分離してテストするには、モックモデルを使用します。references/unit-testing.md を参照してください。

ChatModel mockModel = mock(ChatModel.class);
when(mockModel.generate(any(String.class)))
    .thenReturn(Response.from(AiMessage.from("Mocked response")));

var service = AiServices.builder(AiService.class)
        .chatModel(mockModel)
        .build();
2. テスト依存関係の設定

Maven/Gradle の依存関係を設定します。references/testing-dependencies.md を参照してください。

  • langchain4j-test - ガードレールのアサーション
  • testcontainers - コンテナ化されたテスト
  • mockito - 外部依存関係のモック化
  • assertj - 流れるようなアサーション
3. Testcontainers を使った統合テスト

実際のサービスを使ってテストします。references/integration-testing.md を参照してください。

@Testcontainers
class OllamaIntegrationTest {
    @Container
    static GenericContainer<?> ollama = new GenericContainer<>(
        DockerImageName.parse("ollama/ollama:0.5.4")
    ).withExposedPorts(11434);

    @Test
    void shouldGenerateResponse() {
        // Verify container is healthy
        assertTrue(ollama.isRunning());
        await().atMost(30, TimeUnit.SECONDS)
            .until(() -> ollama.getLogs().contains("API server listening"));

        ChatModel model = OllamaChatModel.builder()
                .baseUrl(ollama.getEndpoint())
                .build();

        // Verify model responds before running tests
        assertDoesNotThrow(() -> model.generate("ping"));

        String response = model.generate("Test query");
        assertNotNull(response);
    }
}
4. 高度な機能

ストリーミング、メモリ、エラー処理のパターンについては、references/advanced-testing.md を参照してください。

5. テストワークフロー

references/workflow-patterns.md のテストピラミッドに従います。

  • 単体テスト 70%:モックを使ったすばやく分離されたテスト
  • 統合テスト 20%:ヘルスチェックを行う実際のサービス
  • エンドツーエンドテスト 10%:ワークフロー全体のテスト
70% Unit Tests ─ Mock ChatModel, guardrails, edge cases
20% Integration Tests ─ Testcontainers, vector stores, RAG
10% End-to-End Tests ─ Complete user journeys
トラブルシューティング
  • コンテナが起動しない:Docker デーモンが実行中か確認し、イメージの存在を確認して、タイムアウトを延長します
  • モデルが応答しない:baseUrl が正しいことを確認し、コンテナログを確認して、モデルが読み込まれていることを確認します
  • テストがタイムアウトする:応答が遅いモデルでは @Timeout の時間を延長し、コンテナのリソース制限を確認します
  • 不安定なテスト:再試行ロジックを追加するか、アサーションの前にヘルスチェックを行います

例

単体テスト
@Test
void shouldProcessQueryWithMock() {
    ChatModel mockModel = mock(ChatModel.class);
    when(mockModel.generate(any(String.class)))
        .thenReturn(Response.from(AiMessage.from("Test response")));

    var service = AiServices.builder(AiService.class)
            .chatModel(mockModel)
            .build();

    String result = service.chat("What is Java?");
    assertEquals("Test response", result);
}
Testcontainers を使った統合テスト
@Testcontainers
class RAGIntegrationTest {
    @Container
    static GenericContainer<?> ollama = new GenericContainer<>(
        DockerImageName.parse("ollama/ollama:0.5.4")
    );

    @BeforeAll
    static void waitForContainerReady() {
        await().atMost(60, TimeUnit.SECONDS)
            .until(() -> ollama.getLogs().contains("API server listening"));
    }

    @Test
    void shouldCompleteRAGWorkflow() {
        assertTrue(ollama.isRunning());

        var chatModel = OllamaChatModel.builder()
                .baseUrl(ollama.getEndpoint())
                .build();

        var embeddingModel = OllamaEmbeddingModel.builder()
                .baseUrl(ollama.getEndpoint())
                .build();

        var store = new InMemoryEmbeddingStore<>();
        var retriever = EmbeddingStoreContentRetriever.builder()
                .chatModel(chatModel)
                .embeddingStore(store)
                .embeddingModel(embeddingModel)
                .build();

        var assistant = AiServices.builder(RagAssistant.class)
                .chatLanguageModel(chatModel)
                .contentRetriever(retriever)
                .build();

        String response = assistant.chat("What is Spring Boot?");
        assertNotNull(response);
        assertTrue(response.contains("Spring"));
    }
}

ベストプラクティス

  • テストを分離するため、@BeforeEach/@AfterEach を使用します
  • 単体テストでは実際の API を呼び出さず、モックを使用します
  • 外部サービスの呼び出しには @Timeout を含めます
  • 成功時とエラー処理の両方のシナリオをテストします
  • 応答の一貫性とエッジケースを検証します

よくあるパターン

モック戦略
ChatModel mockModel = mock(ChatModel.class);
when(mockModel.generate(anyString())).thenReturn(Response.from(AiMessage.from("Mocked")));
when(mockModel.generate(eq("Hello"))).thenReturn(Response.from(AiMessage.from("Hi")));
when(mockModel.generate(contains("Java"))).thenReturn(Response.from(AiMessage.from("Java")));
アサーションヘルパー
assertThat(response).isNotNull().isNotEmpty();
assertThat(response).containsAll(expectedKeywords);
assertThat(response).doesNotContain("error");

参考ドキュメント

制約と警告

  • AI の応答は非決定論的です。信頼性の高い単体テストにはモックを使用します
  • コストやレート制限を避けるため、テストで実際の API 呼び出しを行わないでください
  • 統合テストには Docker が必要です。コンテナのヘルスチェックを使用します
  • RAG テストでは、埋め込みストアに適切なデータを事前に格納する必要があります
  • モックベースのテストでは実際の LLM の動作を保証できないため、統合テストも併用します
  • テスト専用の設定プロファイルを使用し、本番データには決して影響を与えないでください

コメント