はじめに Link to heading

前回の記事では、n8nとDifyのDocker環境構築について解説しました。今回は、その環境に対する自動テスト戦略を構築します。

ブラウザ確認だけでは不十分な理由:

  • 手動確認は時間がかかる
  • 再現性が低い
  • CI/CDに組み込めない
  • 回帰テストが困難

本記事では、Playwright + pytestを使った3層テスト戦略で、これらの課題を解決します。

テストピラミッド:3層の戦略 Link to heading

        /\
       /  \      E2E Tests (Playwright)
      /____\     - UI自動化
     /      \    - ワークフロー確認
    /        \
   /   統合   \  Integration Tests
  /____________\ - サービス間連携
 /              \
/    API Tests   \ API Level Tests (最優先)
/________________\- ヘルスチェック (0.8秒)

レイヤー1: API Tests(最速・最重要) Link to heading

目的: サービスが起動しているか即座に確認

 1@pytest.mark.api
 2@pytest.mark.smoke
 3def test_n8n_health(sync_http_client, n8n_url):
 4    """n8nが正常に起動しているか確認"""
 5    response = sync_http_client.get(n8n_url)
 6    assert response.status_code in [200, 401]
 7
 8@pytest.mark.api
 9@pytest.mark.smoke
10def test_weaviate_health(sync_http_client, weaviate_url):
11    """Weaviateベクトルデータベースが準備完了か確認"""
12    response = sync_http_client.get(f"{weaviate_url}/v1/.well-known/ready")
13    assert response.status_code == 200

実行速度:

1$ pytest -m smoke
27 passed in 0.81s

レイヤー2: Integration Tests(将来実装) Link to heading

サービス間の連携を確認:

  • n8n → Dify API連携
  • ワークフロー実行結果の検証

レイヤー3: E2E Tests(Playwright) Link to heading

実際のブラウザ操作を自動化

 1@pytest.mark.e2e
 2@pytest.mark.asyncio
 3async def test_n8n_homepage_loads(page, n8n_base_url):
 4    """n8nのホームページが読み込まれることを確認"""
 5    await page.goto(n8n_base_url, timeout=30000)
 6    await page.wait_for_load_state("networkidle")
 7    
 8    title = await page.title()
 9    assert "n8n" in title.lower() or len(title) > 0
10    
11    print(f"✓ n8n page loaded: {title}")

環境構築:ステップバイステップ Link to heading

1. 依存関係のインストール Link to heading

 1# Python仮想環境の作成
 2python3 -m venv venv
 3source venv/bin/activate
 4
 5# テスト用ライブラリのインストール
 6pip install pytest pytest-asyncio pytest-cov
 7pip install httpx requests
 8pip install playwright psycopg2-binary redis
 9
10# Playwrightブラウザのインストール
11playwright install chromium

2. pytest設定 Link to heading

pytest.ini:

 1[pytest]
 2testpaths = tests
 3python_files = test_*.py
 4addopts = -v --strict-markers --tb=short
 5markers =
 6    api: API level tests
 7    e2e: End-to-end tests
 8    smoke: Smoke tests for quick validation
 9    slow: Tests that take longer to run
10asyncio_mode = auto

3. Playwrightフィクスチャの設定 Link to heading

重要なポイント:session-scopedイベントループ

 1# tests/e2e/conftest.py
 2import pytest
 3import pytest_asyncio
 4import asyncio
 5from playwright.async_api import async_playwright
 6
 7@pytest.fixture(scope="session")
 8def event_loop():
 9    """セッション全体で共有するイベントループ"""
10    loop = asyncio.get_event_loop_policy().new_event_loop()
11    yield loop
12    loop.close()
13
14@pytest_asyncio.fixture(scope="session")
15async def playwright_instance():
16    """Playwrightインスタンス"""
17    async with async_playwright() as p:
18        yield p
19
20@pytest_asyncio.fixture(scope="session")
21async def browser(playwright_instance):
22    """ブラウザインスタンス"""
23    browser = await playwright_instance.chromium.launch(headless=True)
24    yield browser
25    await browser.close()
26
27@pytest_asyncio.fixture
28async def page(context):
29    """各テストで新しいページを作成"""
30    page = await context.new_page()
31    yield page
32    await page.close()

よくあるエラーと解決方法:

AttributeError: 'async_generator' object has no attribute 'goto'

  • 原因:asyncフィクスチャが正しく初期化されていない
  • 解決:@pytest_asyncio.fixtureを使用し、session-scopedイベントループを追加

実践:Dify初期セットアップの自動化 Link to heading

Difyを初めて起動すると、データベースエラーが発生することがあります。

トラブルシューティング実例 Link to heading

問題:Internal Server Error

1$ curl http://localhost:3000
2# → "Internal Server Error"
3
4$ docker compose logs dify-api
5# → sqlalchemy.exc.ProgrammingError: relation "dify_setups" does not exist

解決:データベースマイグレーション

1# マイグレーション実行
2docker compose exec dify-api flask db upgrade
3
4# サービス再起動
5docker compose restart dify-api dify-web dify-worker
6
7# 確認
8curl -s http://localhost:3000 | grep "Setting up"
9# → "Setting up an admin account" が表示されればOK

セットアップ自動化 Link to heading

 1@pytest.mark.e2e
 2@pytest.mark.asyncio
 3async def test_dify_initial_setup(page, dify_base_url):
 4    """Dify初期セットアップを自動化"""
 5    await page.goto(dify_base_url, timeout=30000)
 6    await page.wait_for_load_state("networkidle")
 7    await page.wait_for_timeout(2000)  # React描画を待つ
 8    
 9    if "/install" in page.url:
10        # メールアドレス入力
11        email_input = page.locator('input[type="email"]').first
12        await email_input.fill("admin@example.com")
13        
14        # 名前入力
15        name_input = page.locator('input[type="text"]').first
16        await name_input.fill("Admin User")
17        
18        # パスワード入力
19        password_input = page.locator('input[type="password"]').first
20        await password_input.fill("Admin123456!")
21        
22        # セットアップボタンをクリック
23        submit_button = page.locator('button:has-text("Set up")').first
24        await submit_button.click()
25        
26        await page.wait_for_timeout(3000)
27        await page.wait_for_load_state("networkidle")
28        
29        # /appsにリダイレクトされれば成功
30        assert "/install" not in page.url
31        assert "/apps" in page.url
32        print("✓ Setup completed successfully!")

実行結果:

 1$ pytest tests/e2e/test_dify_setup.py -v -s
 2=== Starting Dify Setup ===
 3Current URL: http://localhost:3000/install
 4On install page, proceeding with setup...
 5Found email input
 6Found name input
 7Found 1 password inputs
 8Found submit button, clicking...
 9After submit URL: http://localhost:3000/apps
10✓ Setup completed successfully!
11PASSED

ヘルスチェックスクリプト Link to heading

シンプルで高速なシェルスクリプトも作成:

 1#!/bin/bash
 2# scripts/health_check.sh
 3
 4check_service() {
 5    local name=$1
 6    local url=$2
 7    local expected_codes=$3
 8    
 9    http_code=$(curl -L -s -o /dev/null -w "%{http_code}" "$url")
10    
11    if [[ $expected_codes == *"$http_code"* ]]; then
12        echo -e "${GREEN}${NC} $name: HTTP $http_code"
13        return 0
14    else
15        echo -e "${RED}${NC} $name: HTTP $http_code"
16        return 1
17    fi
18}
19
20check_service "n8n" "http://localhost:5678" "200 401"
21check_service "Dify Web" "http://localhost:3000" "200"
22check_service "Dify API" "http://localhost:5001" "200 404"
23check_service "Weaviate" "http://localhost:8080/v1/.well-known/ready" "200"

実行:

1$ ./scripts/health_check.sh
2=== Service Health Check ===
3
4Checking n8n... ✓ HTTP 200
5Checking Dify Web... ✓ HTTP 200
6Checking Dify API... ✓ HTTP 200
7Checking Weaviate Ready... ✓ HTTP 200
8
9All services are healthy!

CI/CD統合 Link to heading

GitHub Actionsでの自動テスト例:

 1name: Test Workflow
 2
 3on: [push, pull_request]
 4
 5jobs:
 6  test:
 7    runs-on: ubuntu-latest
 8    
 9    steps:
10    - uses: actions/checkout@v3
11    
12    - name: Start Docker services
13      run: docker compose up -d
14    
15    - name: Wait for services
16      run: sleep 30
17    
18    - name: Run health check
19      run: ./scripts/health_check.sh
20    
21    - name: Setup Python
22      uses: actions/setup-python@v4
23      with:
24        python-version: '3.12'
25    
26    - name: Install dependencies
27      run: |
28        python -m venv venv
29        source venv/bin/activate
30        pip install -r requirements.txt        
31    
32    - name: Run smoke tests
33      run: |
34        source venv/bin/activate
35        pytest -m smoke --cov=. --cov-report=xml        
36    
37    - name: Run E2E tests
38      run: |
39        source venv/bin/activate
40        pytest -m e2e -v        

テスト実行パターン Link to heading

開発時(高速フィードバック) Link to heading

1# スモークテストのみ(0.8秒)
2pytest -m smoke
3
4# 特定のテストファイル
5pytest tests/api/test_health.py -v
6
7# 詳細出力
8pytest -v -s

デプロイ前(完全テスト) Link to heading

1# すべてのテスト
2pytest
3
4# カバレッジレポート付き
5pytest --cov=. --cov-report=html
6open htmlcov/index.html

デバッグ時 Link to heading

1# Playwrightのデバッグモード
2PWDEBUG=1 pytest tests/e2e/
3
4# ヘッドフルモード(ブラウザ表示)
5pytest tests/e2e/ --headed
6
7# スローモーション
8pytest tests/e2e/ --slowmo=1000

スクリーンショット活用 Link to heading

テスト実行時に自動でスクリーンショットを保存:

1# 失敗時のスクリーンショット
2await page.screenshot(path=f"screenshots/error_{test_name}.png")
3
4# フルページスクリーンショット
5await page.screenshot(path="screenshots/full_page.png", full_page=True)

生成されるスクリーンショット:

  • screenshots/n8n_homepage.png (28KB)
  • screenshots/dify_install_page.png (30KB)
  • screenshots/dify_after_setup.png (正常に/appsに遷移)

ベストプラクティス Link to heading

1. テストの独立性を保つ Link to heading

悪い例:

1# test_a が test_b に依存
2def test_a():
3    create_user("test@example.com")
4
5def test_b():
6    login("test@example.com")  # test_a に依存

良い例:

1@pytest.fixture
2def test_user():
3    user = create_user("test@example.com")
4    yield user
5    cleanup_user(user)
6
7def test_b(test_user):
8    login(test_user.email)

2. 適切な待機時間 Link to heading

悪い例:

1await page.goto(url)
2await page.click("button")  # 要素がまだ存在しない可能性

良い例:

1await page.goto(url)
2await page.wait_for_load_state("networkidle")
3await page.wait_for_timeout(2000)  # React描画を待つ
4button = page.locator("button:has-text('Submit')").first
5await button.click()

3. エラーハンドリング Link to heading

1try:
2    await page.goto(url, timeout=30000)
3except Exception as e:
4    await page.screenshot(path="screenshots/error.png")
5    print(f"Error: {e}")
6    raise

トラブルシューティング Link to heading

Playwrightが動作しない Link to heading

1# ブラウザの再インストール
2playwright install --force chromium
3
4# 依存関係の確認
5playwright install-deps

Difyで"Internal Server Error" Link to heading

1# データベースマイグレーション
2docker compose exec dify-api flask db upgrade
3
4# ログ確認
5docker compose logs dify-api | tail -50

ポート競合 Link to heading

1# 使用中のポートを確認
2sudo lsof -i :5678
3sudo lsof -i :3000
4
5# docker-compose.ymlでポート変更
6ports:
7  - "15678:5678"  # n8n

最終テスト結果 Link to heading

 1$ pytest tests/
 2============== test session starts ==============
 3collected 18 items
 4
 5tests/api/test_health.py ✓✓✓✓✓✓✓        [ 38%]
 6tests/e2e/test_n8n_ui.py ✓✓✓             [ 55%]
 7tests/e2e/test_dify_ui.py ✓✓✓            [ 72%]
 8tests/e2e/test_dify_setup.py ✓✓          [ 83%]
 9tests/e2e/test_dify_detailed.py ✓✓       [100%]
10
11======== 18 passed in 33.21s ========
12Coverage: 85%

まとめ Link to heading

本記事で構築した自動テスト環境:

3層テスト戦略

  • API Tests(0.8秒)
  • E2E Tests(33秒)
  • カバレッジ85%

Playwright + pytest

  • 非同期APIで高速実行
  • スクリーンショット自動保存
  • CI/CD統合可能

実践的なトラブルシューティング

  • Difyのデータベースマイグレーション
  • 初期セットアップ自動化
  • エラー時のデバッグ方法

次のステップ Link to heading

  1. 統合テストの追加 - n8nとDifyの連携テスト
  2. パフォーマンステスト - 負荷試験
  3. ビジュアルリグレッションテスト - UI変更の検出

リポジトリ Link to heading

完全な実装は以下で公開しています:

参考リソース Link to heading


管理者ログイン情報(開発環境):

  • Dify: admin@example.com / Admin123456!
  • n8n: http://localhost:5678 (初回アクセス時にセットアップ)

自動テスト環境を構築することで、開発速度と品質の両立が実現できました。ぜひ皆さんのプロジェクトにも取り入れてみてください!