kickflow Tech Blog

株式会社kickflowのプロダクト開発本部によるブログ

AutifyとQaseを連携して自動テスト結果管理を自動化する

カレドニアガラスは「因果関係を理解して道具を作る動物」であると言われる

こんにちは、kickflow QAチームの川村です。

E2Eテストの実行は自動化できても、その結果を管理し、確認するプロセスは意外と手作業が残りがちです。
今回は、Autifyで実行した350を超えるテストシナリオの結果を、テスト管理ツールのQaseに自動連携する仕組みを構築した話をします。

テスト結果の管理を自動化することで、結果確認フローが大幅に効率化され、QAチームは本来注力すべき品質改善活動に集中できるようになりました。

背景と課題

kickflowでは、E2Eテストの実行にAutifyを使用しています。
毎日のリグレッションテストでは350以上のシナリオを実行しており、その結果を適切に管理することが課題でした。

従来の課題

従来は、SlackのAutify結果通知スレッドに再実行結果を手動で貼り付ける運用をしていました。

Slack通知に再実行結果のリンクを貼っていた

しかし、シナリオ数が増えるにつれてこの運用には限界がありました。

  • 350件以上のシナリオがあると、Slackのスレッドでは結果が埋もれてしまう
  • 過去の実行履歴を遡って確認するのが困難
  • Flaky Testの傾向把握ができない
  • 再実行結果の手動貼り付けで入力ミスや漏れが発生

手動テストのテスト管理ツールとしてQaseを導入していたので、Autifyの結果もQaseで一元管理できれば、これらの課題を解決できると考えました。
そこで、Qase上にAutify用のプロジェクトを新規作成し、最初から自動連携を前提としたシステムを構築することにしました。

構築したシステムの概要

この課題を解決するため、AutifyとQaseを自動連携するシステムを構築しました。
以下の4つの機能を実現しています。

  1. テストシナリオの自動同期(毎日0:00 JST)
  2. テスト結果の自動連携(Autify実行完了時)
  3. 失敗シナリオの自動再実行(毎朝7:30 JST)
  4. 再実行結果の自動反映

Flaky Testへの対策として、失敗したシナリオの再実行まで自動化しています。

アーキテクチャ

sequenceDiagram
    participant Autify
    participant Zapier
    participant GitHub Actions
    participant Qase
    participant Slack

    Note over Autify,Qase: テスト結果の自動連携フロー

    Autify->>Autify: テスト実行完了
    Autify->>Zapier: Webhook送信
    Zapier->>GitHub Actions: repository_dispatch
    GitHub Actions->>GitHub Actions: process-webhook.js実行
    GitHub Actions->>Autify: テスト結果詳細を取得
    Autify-->>GitHub Actions: 結果データ
    GitHub Actions->>Qase: TestRun作成 & 結果登録
    Qase-->>GitHub Actions: 完了
    GitHub Actions->>Slack: 通知

    Note over Autify,Qase: 毎日のシナリオ同期フロー

    GitHub Actions->>GitHub Actions: 定期実行(0:00 JST)
    GitHub Actions->>Autify: 全シナリオ取得
    Autify-->>GitHub Actions: シナリオ一覧
    GitHub Actions->>Qase: テストケース同期
    Qase-->>GitHub Actions: 完了

Zapierを経由してGitHub Actionsをトリガーする構成にしたのは、中間サーバーを自作しなくて済むからです。

機能詳細

1. テストシナリオの自動同期

毎日0:00(JST)に、Autifyのシナリオ一覧とQaseのテストケースを同期します。

// src/services/sync.service.ts の主要部分(簡略化)

async performScenarioSync(autifyScenarios, qaseTestCases) {
  // QaseテストケースをAutify IDでマッピング
  const qaseTestCaseByAutifyId = new Map()
  qaseTestCases.forEach(testCase => {
    const autifyField = testCase.custom_fields.find(f => f.id === 8)
    if (autifyField?.value) {
      qaseTestCaseByAutifyId.set(String(autifyField.value), testCase)
    }
  })

  for (const scenario of autifyScenarios) {
    const existingTestCase = qaseTestCaseByAutifyId.get(String(scenario.id))

    if (existingTestCase) {
      // 既存のテストケースを更新
      await this.qaseService.updateTestCase(existingTestCase.id, {
        title: scenario.name,
        custom_fields: { '8': scenario.id.toString() }
      })
    } else {
      // 新しいテストケースを作成
      await this.qaseService.createTestCase({
        title: scenario.name,
        custom_fields: { '8': scenario.id.toString() }
      })
    }
  }
}

同期処理では、以下のマッピングを行っています。

処理 説明
新規作成 Autifyにあり、Qaseにないシナリオ
更新 名前が変更されたシナリオ
削除 Autifyから削除されたシナリオ

Autifyのシナリオには固有のIDがあるため、QaseのテストケースにカスタムフィールドとしてAutify IDを保存し、確実にマッピングしています。

2. テスト結果の自動連携

Autifyでテストプランの実行が完了すると、Webhookを経由して自動的にQaseにTestRunが作成されます。

// scripts/process-webhook.js の主要部分(簡略化)

async handleTestResultCompleted(resultId) {
  // Autifyからテスト結果を取得
  const testResult = await this.autifyService.getTestResult(resultId);
  const capabilityResults = testResult.test_plan_capability_results;

  // QaseにTestRunを作成
  const testRun = await this.qaseService.createTestRun({
    title: `${startedAtJST}_${testPlan.name}`,
    description: `Autify Result ID: ${testResult.id}`,
    cases: testCases
  });

  // 各シナリオの結果を登録
  for (const result of testResults) {
    await this.qaseService.addTestResult(testRun.id, result.testCaseId, {
      status: this.mapAutifyStatusToQase(result.status),
      comment: `[${autifyLink}](${autifyLink})`,
      time_spent: Math.round(result.duration / 1000)
    });
  }
}

Autifyの結果画面へのリンクもコメントとして自動挿入されるため、詳細を確認したいときにすぐアクセスできます。

Qaseのテスト結果画面にAutifyの結果リンクが自動で添付される

シナリオごとの過去の実行履歴も確認できます。

Qaseのテストケース単位のRun History

ステータスマッピング

AutifyとQaseではステータスの表現が異なるため、以下のようにマッピングしています。
Qaseの標準ステータスだけでは表現しきれないため、review_neededretest_passedretest_failedなどのカスタムステータスを作成しています。

Autifyステータス Qaseステータス 説明
passed passed 成功
failed failed 失敗
internal_error internal_error(カスタム) 内部エラー
review_needed(passed) review_needed(カスタム) 要確認(成功だがレビュー必要)

review_neededは、Autifyで「要確認ステップ」を含むシナリオが成功した場合に設定されます。
このステータスも再実行対象に含めることで、目視確認が必要なシナリオを漏れなく再実行できるようにしています。

Qaseで利用しているステータス一覧

3. 失敗シナリオの自動再実行

平日の朝7:30に、Nightlyで実行したテストプランの失敗シナリオを自動的にAutifyで再実行します。

// src/controllers/rerun.controller.ts の主要部分(簡略化)

async rerunFailedScenarios() {
  // 進行中のTestRunから失敗したテスト結果を取得
  const testRun = await this.qaseService.findLatestSandboxRegressionTestRun()
  const testResults = await this.qaseService.getTestResults(testRun.id)

  // 各テストケースの最新結果のみを抽出
  const latestResultsByCase = new Map()
  for (const result of testResults) {
    const existing = latestResultsByCase.get(result.case_id)
    if (!existing || new Date(result.end_time) > new Date(existing.end_time)) {
      latestResultsByCase.set(result.case_id, result)
    }
  }

  // 再実行対象のステータスでフィルタリング
  const rerunTargetStatuses = ['failed', 'retest_failed', 'review_needed', 'internal_error']
  const failedResults = [...latestResultsByCase.values()]
    .filter(r => rerunTargetStatuses.includes(r.status))

  // AutifyシナリオIDを取得して再実行
  const scenarioIds = failedResults.map(r => getAutifyScenarioId(r.case_id))
  await this.autifyService.rerunMultipleScenarios(scenarioIds)
}

Flaky Test対策として、失敗したシナリオを1回だけ自動再実行する仕組みです。
再実行対象のステータスは failedretest_failedinternal_error などです。

また、失敗シナリオが150件以上ある場合は環境障害の可能性が高いため、自動再実行をスキップする閾値を設けています。

4. 再実行結果の自動反映

再実行が完了すると、その結果も自動的にQaseに反映されます。
再実行の結果は、通常の実行結果とは区別するため、専用のステータスを使用しています。

再実行結果 Qaseステータス
成功 retest_passed
失敗 retest_failed

これにより、初回実行で失敗したが再実行で成功したケース(Flaky Test)や、2回目も失敗したケース(担当者の目視での確認が必要)を簡単にラベリングできます。

QaseのTestRun画面

実際にQaseのTestRun画面では、以下のように結果が可視化されます。

  • TestRunのタイトルには実行日時とテストプラン名が自動設定
  • 各テストケースにはAutifyへのリンクが付与
  • ステータスで色分けされ、失敗箇所が一目瞭然

QaseのTestRun一覧画面
QaseのTestRun詳細画面

工夫したポイント

1. キャッシュによるAPI呼び出しの最適化

Qaseのテストケースを毎回API経由で取得すると、350件以上のケースに対してレートリミットに抵触するリスクがありました。
そこで、テストケースの情報をJSONファイルにキャッシュし、毎日の同期時に更新する仕組みを導入しました。

// src/services/cache.service.ts の主要部分(簡略化)

interface TestCaseCache {
  lastUpdated: string
  testCases: Array<{ id: number; title: string; autifyScenarioId?: string }>
  autifyIdMap: Record<string, number>  // Autify ID → Qase Test Case ID
}

// キャッシュ保存時にマッピングを構築
saveCache(testCases: TestCase[]): void {
  const autifyIdMap: Record<string, number> = {}
  testCases.forEach(tc => {
    const autifyField = tc.custom_fields?.find(f => f.id === 8)
    if (autifyField?.value) {
      autifyIdMap[String(autifyField.value)] = tc.id
    }
  })

  const cache: TestCaseCache = {
    lastUpdated: new Date().toISOString(),
    testCases: testCases.map(tc => ({
      id: tc.id,
      title: tc.title,
      autifyScenarioId: tc.custom_fields?.find(f => f.id === 8)?.value
    })),
    autifyIdMap
  }
  fs.writeFileSync(CACHE_FILE_PATH, JSON.stringify(cache, null, 2))
}

// Autify IDから高速にQaseテストケースIDを取得
findTestCaseByAutifyId(autifyId: string): number | undefined {
  return this.cache?.autifyIdMap[autifyId]
}

2. 進行中TestRunの重複防止

同じテストプランを短時間に複数回実行した場合、TestRunが重複作成されないよう、既存の進行中TestRunを検索して更新する処理を実装しています。
Autifyにはテストプランで失敗したシナリオのみを再実行する機能がありますが、以下の重複防止のロジックを入れておかないと、Qaseに新しいTestRunが切られてしまいます。

// 既存のTestRunをチェック
const existingTestRun = inProgressTestRuns.find(run => {
  const titleMatch = run.title.match(/\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}_(.+)$/);
  const extractedPlanName = titleMatch ? titleMatch[1] : run.title;
  return extractedPlanName === testPlan?.name;
});

if (existingTestRun) {
  // 既存のTestRunを更新
  await this.updateExistingTestRun(existingTestRun, testResults);
} else {
  // 新しいTestRunを作成
  await this.createNewTestRun(...);
}

3. Slack通知との連携

TestRun作成時や再実行完了時にはSlackに通知を送信し、チームメンバーがすぐに結果を確認できるようにしています。

Qase作成時のSlack通知

再実行トリガー時のSlack通知

得られた成果

このシステムを導入したことで、以下の成果が得られました。

  1. 作業時間の削減

    • 毎日1時間程度 → 30分程度
    • 再実行結果の管理作業が自動化されたことで、担当者は原因調査やシナリオ保守に集中できるように
  2. 入力ミスの解消

    • 手作業による転記ミスがなくなった
    • 担当者が無駄な作業に神経を使わなくて済む
  3. トレーサビリティの向上

    • AutifyとQaseが確実に連携
    • 過去の実行履歴も追跡可能
  4. Flaky Testの可視化

    • 再実行ステータスにより、不安定なテストを特定しやすくなった

今後の展望

kickflowのQAチームでは、Autify NoCode Web以外にもAutify Nexus、Cypress、Playwright、PostmanといったツールでE2Eテストを実行しています。
現在はAutify NoCode Webのみがこの仕組みに対応していますが、他のツールのテストシナリオが増えてきた際には、すべてをQaseで一元管理できるようにしたいと考えています。

そうなれば、複数ツールの結果確認を1つの画面で完結でき、さらに効率化が進むはずです。

まとめ

今回は、AutifyとQaseを連携してテスト結果の管理を自動化した事例をご紹介しました。

E2Eテストの自動化は進んでいても、その結果の管理は手作業のまま、というチームは多いのではないでしょうか。
テスト管理の自動化は地味な取り組みですが、日々の作業負荷を大幅に軽減し、QAチームが本来注力すべき品質改善活動に集中できる環境を作る上で非常に重要です。

なお、本システムの95%以上はAI(Claude)を活用して開発しました。
おかげで、他の業務を進めている裏側でシステム構築が完了し、短期間でリリースすることができました。
AIの活用によって、こうした業務効率化ツールの開発ハードルが大幅に下がっていると感じています。

この記事が、同様の課題を抱えているチームの参考になれば幸いです。


kickflowでは、このようにテスト自動化やQAプロセスの改善に取り組んでいます。
品質とエンジニアリングの両方に興味がある方、一緒に働いてみませんか?

careers.kickflow.co.jp