第5章:ランナー詳細編 - 6大プロトコルを完全制覇!

ついに来た、runnの真骨頂! 他のテストツールが一つのプロトコルしか扱えない中、runnは6つのプロトコルを統一的に操る!この章では、各ランナーの秘伝の技を伝授しよう!

🎆 6大ランナー軍団 - あなたの最強の武器庫!

runnは6つの強力なランナーを搭載!それぞれが特定のプロトコルのスペシャリストだ!

ランナー プロトコル 用途
🌐 HTTP HTTP/HTTPS REST API、GraphQL、Webhookを完全制圧
🔗 gRPC gRPC マイクロサービス間通信を爆速テスト
🗄️ DB SQL データベースを自在に操作
🌐 CDP Chrome DevTools Protocol ブラウザを完全支配
💻 SSH SSH リモートサーバーを思いのままに
⚙️ Exec プロセス実行 ローカルコマンドを瞬時に実行

🌐 HTTPランナー - Web APIテストの王者!

🚀 基本的な設定 - まずはここから始めよう!

desc: HTTPランナーの基本設定
runners:
  api: https://api.example.com/v1
  # 複数のエンドポイントを定義可能
  auth: https://auth.example.com
  webhook: http://localhost:8080/webhook

🎨 リクエストメソッドとパラメータ - あらゆるHTTPメソッドを制覇!

desc: HTTPリクエストメソッドとパラメータの例
runners:
  httpbin: http://localhost:8080
steps:
  # GET リクエスト
  get_request:
    httpbin:
      /get?page=1&limit=10&sort=created_at:
        get:
          headers:
            Accept: application/json
            User-Agent: runn/test
    test: |
      current.res.status == 200 &&
      current.res.body.args.page[0] == "1" &&
      current.res.body.args.limit[0] == "10" &&
      current.res.body.args.sort[0] == "created_at"

  # POST リクエスト
  post_request:
    httpbin:
      /post:
        post:
          headers:
            Content-Type: application/json
          body:
            application/json:
              name: "Alice"
              email: "alice@example.com"
              role: "user"
    test: |
      current.res.status == 200 &&
      current.res.body.json.name == "Alice" &&
      current.res.body.json.email == "alice@example.com" &&
      current.res.body.json.role == "user"

  # PUT リクエスト
  put_request:
    httpbin:
      /put:
        put:
          body:
            application/json:
              name: "Alice Smith"
              email: "alice.smith@example.com"
              id: 123
    test: |
      current.res.status == 200 &&
      current.res.body.json.name == "Alice Smith" &&
      current.res.body.json.email == "alice.smith@example.com"

  # PATCH リクエスト
  patch_request:
    httpbin:
      /patch:
        patch:
          body:
            application/json:
              role: "admin"
    test: |
      current.res.status == 200 &&
      current.res.body.json.role == "admin"

  # DELETE リクエスト
  delete_request:
    httpbin:
      /delete:
        delete: {}
    test: current.res.status == 200

  # Bearer認証の例
  auth_request:
    httpbin:
      /bearer:
        get:
          headers:
            Authorization: "Bearer test-token-123"
    test: |
      current.res.status == 200 &&
      current.res.body.authenticated == true &&
      current.res.body.token == "test-token-123"

📦 様々なボディ形式 - どんなデータ形式もお手のもの!

desc: 様々なボディ形式のHTTPリクエスト
runners:
  httpbin: http://localhost:8080
steps:
  # JSON形式
  json_request:
    httpbin:
      /post:
        post:
          body:
            application/json:
              key: "value"
              nested:
                array: [1, 2, 3]
    test: |
      current.res.status == 200 &&
      current.res.body.headers["Content-Type"][0] == "application/json" &&
      current.res.body.json.key == "value" &&
      current.res.body.json.nested.array[0] == 1 &&
      current.res.body.json.nested.array[1] == 2 &&
      current.res.body.json.nested.array[2] == 3

  # フォームデータ
  form_request:
    httpbin:
      /post:
        post:
          body:
            application/x-www-form-urlencoded:
              username: alice
              password: secret123
    test: |
      current.res.status == 200 &&
      current.res.body.headers["Content-Type"][0] == "application/x-www-form-urlencoded" &&
      current.res.body.form.username[0] == "alice" &&
      current.res.body.form.password[0] == "secret123"

  # マルチパートフォーム(テストデータ用の文字列フィールドのみ)
  multipart_request:
    httpbin:
      /post:
        post:
          body:
            multipart/form-data:
              field1: "test value"
              description: "Test multipart form"
    test: |
      current.res.status == 200 &&
      current.res.body.headers["Content-Type"][0] contains "multipart/form-data" &&
      current.res.body.form.field1[0] == "test value" &&
      current.res.body.form.description[0] == "Test multipart form"

  # プレーンテキスト
  text_request:
    httpbin:
      /post:
        post:
          headers:
            Content-Type: text/plain
          body:
            text/plain: |
              This is a plain text message
              with multiple lines
    test: |
      current.res.status == 200 &&
      current.res.body.headers["Content-Type"][0] == "text/plain" &&
      current.res.body.data contains "This is a plain text message" &&
      current.res.body.data contains "with multiple lines"

  # カスタムヘッダーとJSONボディ
  custom_headers_request:
    httpbin:
      /anything:
        put:
          headers:
            X-Custom-Header: "custom-value"
            Authorization: "Bearer token123"
          body:
            application/json:
              action: "update"
              id: 42
    test: |
      current.res.status == 200 &&
      current.res.body.method == "PUT" &&
      current.res.body.headers["X-Custom-Header"][0] == "custom-value" &&
      current.res.body.headers["Authorization"][0] == "Bearer token123" &&
      current.res.body.json.action == "update" &&
      current.res.body.json.id == 42

🔗 gRPCランナー - マイクロサービスの強い味方!

TODO: grpc の例を追加する

🗄️ データベースランナー - SQLの魔術師になれ!

🌍 対応データベース - あらゆるDBを制覇!

desc: 対応データベースの接続設定
runners:
  # PostgreSQL
  postgres: postgres://user:password@localhost:5432/testdb?sslmode=disable

  # MySQL
  mysql: mysql://user:password@localhost:3306/testdb

  # SQLite
  sqlite: sqlite:///path/to/database.db

  # Cloud Spanner
  spanner: spanner://projects/my-project/instances/my-instance/databases/my-db

📝 基本的なクエリ操作 - SQLを思いのままに!

desc: 基本的なデータベースクエリ操作
runners:
  mydb:
    dsn: sqlite:///tmp/db_basic_queries.db
    trace: true

vars:
  test_email: "test@example.com"

steps:
  create_table:
    mydb:
      query: |
        DROP TABLE IF EXISTS users;
        CREATE TABLE users (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          name TEXT NOT NULL,
          email TEXT NOT NULL UNIQUE,
          active BOOLEAN DEFAULT true,
          password_hash TEXT NOT NULL,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )

  # INSERT クエリ
  insert_user:
    mydb:
      query: |
        INSERT INTO users (name, email, password_hash)
        VALUES ('{{ faker.Username() }}', '{{ faker.Email() }}', '{{ toBase64(faker.Password(true, true, true, false, false, 12)) }}')
    dump: current
    test: |
      current.last_insert_id == 1

  # SELECT クエリ
  select_users:
    mydb:
      query: |
        SELECT id, name, email, created_at
        FROM users
    test: |
      len(current.rows) <= 10

  # UPDATE クエリ
  update_user:
    mydb:
      query: |
        UPDATE users
        SET name = 'Updated name', updated_at = CURRENT_TIMESTAMP
        WHERE id = {{ steps.insert_user.last_insert_id }}
    dump: current
    test: |
      current.rows_affected == 1

  # DELETE クエリ
  delete_user:
    mydb:
      query: |
        DELETE FROM users
        WHERE id = {{ steps.insert_user.last_insert_id }}
    test: current.rows_affected == 1

🌐 CDPランナー(ブラウザ自動化) - ブラウザを完全支配!

主なCDP actions一覧

アクション名 概要
attributes 要素の属性取得
click 要素をクリック
doubleClick 要素をダブルクリック
evaluate JS式の評価
fullHTML ページ全体のHTML取得
innerHTML 要素のinnerHTML取得
localStorage localStorage取得
location 現在のURL取得
navigate 指定URLへ遷移
outerHTML 要素のouterHTML取得
screenshot スクリーンショット取得
scroll 要素までスクロール
sendKeys 要素にキー入力
sessionStorage sessionStorage取得
setUploadFile ファイルアップロード
setUserAgent User-Agent設定
submit フォーム送信
tabTo タブ切り替え
text 要素のテキスト取得
textContent 要素のtextContent取得
title ページタイトル取得
value 要素のvalue取得
wait 指定時間待機
waitReady 要素の準備完了まで待機
waitVisible 要素の表示まで待機

※詳細・最新情報は公式READMEをご参照ください。

🎮 基本的な使い方

desc: ブラウザ自動化テストの基本
runners:
  cdp: chrome://new  # 新しいChromeインスタンスを起動

steps:
  # ページナビゲーション
  navigate_to_page:
    cdp:
      actions:
        - navigate: https://pkg.go.dev/time
        - click: 'body > header > div.go-Header-inner > nav > div > ul > li:nth-child(2) > a'
        - waitVisible: 'body > footer'
        - text: 'h1'
    dump: current
    test: |
      current.text == 'Install the latest version of Go'

💻 SSHランナー - リモートサーバーの絶対的支配者!

🔑 基本的な設定 - サーバーへのセキュアアクセス!

desc: SSH経由でのリモート操作
runners:
  server: ssh://user@example.com:22
  # 秘密鍵を使用する場合
  secure_server:
    type: ssh
    addr: user@secure.example.com:22
    key: /path/to/private_key
    passphrase: "{{ env.SSH_PASSPHRASE }}"
env:
  SSH_PASSPHRASE: "${SSH_PASSPHRASE:-dummy-passphrase}"
steps:
  # 基本的なコマンド実行
  basic_command:
    ssh:
      server:///
        command: ls -la /home/user
    test: |
      current.exit_code == 0 &&
      current.stdout contains "total"

  # 複数コマンドの実行
  multiple_commands:
    ssh:
      server:///
        command: |
          cd /var/log
          ls -la *.log | head -5
          df -h
    test: current.exit_code == 0

  # ファイル操作
  file_operations:
    ssh:
      server:///
        command: |
          echo "Test content" > /tmp/test.txt
          cat /tmp/test.txt
          rm /tmp/test.txt
    test: |
      current.exit_code == 0 &&
      current.stdout contains "Test content"

  # システム情報の取得
  system_info:
    ssh:
      server:///
        command: |
          uname -a
          uptime
          free -m
          ps aux | head -10
    test: current.exit_code == 0
    dump:
      system_output: current.stdout

📏 サーバー監視とヘルスチェック - 24時間365日の番人!

desc: サーバー監視とヘルスチェック
runners:
  server: ssh://user@example.com:22
steps:
  health_check:
    ssh:
      server:///
        command: |
          # サービスの状態確認
          systemctl is-active nginx
          systemctl is-active postgresql

          # ポートの確認
          netstat -tlnp | grep :80
          netstat -tlnp | grep :5432

          # ディスク使用量
          df -h | grep -E '(Filesystem|/dev/)'

          # メモリ使用量
          free -m

          # CPU負荷
          uptime
    test: |
      current.exit_code == 0 &&
      current.stdout contains "active" &&
      !(current.stdout contains "failed")

    dump:
      health_status: |
        {
          "services_active": current.stdout contains "active",
          "ports_open": current.stdout contains ":80" && current.stdout contains ":5432",
          "timestamp": time.now()
        }

⚙️ Execランナー(ローカルコマンド実行) - シェルコマンドの魔術師!

🚀 基本的な使用方法 - コマンドを瞬時に実行!

desc: ローカルコマンドの実行
env:
  PATH: "${PATH}"
steps:
  # 基本的なコマンド実行
  basic_exec:
    exec:
      command: echo "Hello, World!"
    test: |
      current.exit_code == 0 &&
      current.stdout == "Hello, World!\n"

  # 環境変数を設定してコマンド実行
  exec_with_env:
    exec:
      command: env | grep TEST_VAR
      env:
        TEST_VAR: "test_value"
        PATH: "{{ env.PATH }}"
    test: |
      current.exit_code == 0 &&
      current.stdout contains "TEST_VAR=test_value"

  # 作業ディレクトリを指定
  exec_with_workdir:
    exec:
      command: pwd
      dir: /tmp
    test: current.stdout contains "/tmp"

  # 複雑なシェルコマンド
  complex_shell:
    exec:
      command: |
        for i in {1..5}; do
          echo "Count: $i"
        done | grep "Count: [35]"
    test: |
      current.exit_code == 0 &&
      current.stdout contains "Count: 3" &&
      current.stdout contains "Count: 5"

📁 ファイル操作とテスト - ローカルファイルを完全管理!

desc: ファイル操作とテスト
steps:
  # テストファイルの作成
  create_test_file:
    exec:
      command: |
        cat > /tmp/test_data.json << 'EOF'
        {
          "users": [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"}
          ]
        }
        EOF
    test: current.exit_code == 0

  # ファイル内容の検証
  validate_file:
    exec:
      command: cat /tmp/test_data.json
    test: |
      current.exit_code == 0 &&
      fromJSON(current.stdout).users[0].name == "Alice"

  # ファイルのクリーンアップ
  cleanup:
    exec:
      command: rm -f /tmp/test_data.json
    test: current.exit_code == 0

🎆 まとめ - ランナーマスター誕生!

やったぞ! あなたは今、6大ランナーを完全にマスターした!

🏆 この章で身につけた7つの必殺技:

  1. 🌐 HTTPランナー: REST API、GraphQL、認証フローを完全制圧
  2. 🔗 gRPCランナー: マイクロサービス間通信を爆速テスト
  3. 🗄️ DBランナー: データベースを思いのままに操作
  4. 🌐 CDPランナー: ブラウザを完全自動化
  5. 💻 SSHランナー: リモートサーバーを自由自在に監視
  6. ⚙️ Execランナー: ローカルコマンドを瞬時に実行
  7. 🌈 ランナーの組み合わせ: マルチプロトコルテストの達人に!

各ランナーを絶妙に組み合わせれば、どんなに複雑なシステムも完璧にテストできる。あなたはもう、プロトコルの支配者だ!