QualityForwardで実現するCI/CD実践入門

## はじめに
ソフトウェアの品質向上を目指す中で、テストの自動化と管理は非常に重要な課題です。
多くの現場では、テストケースやテスト結果をExcelやスプレッドシートで管理しているのが一般的ですが、手作業が多く非効率な部分が指摘されています。
一方で、Google Apps Scriptなどを活用した自動化の取り組みによって、管理の柔軟性や効率が向上してきました。しかし、これだけでは不十分であり、データの一元管理やリアルタイム性といった視点もますます求められるようになっています。
さらに、テスト管理ツールの選択肢やシステム全体との統合といった点が、これからの課題として浮き彫りになっています。

今回、ベリサーブ社が提供するテスト管理ツールである「**Quality Forward**」の [**フリープラン**](https://www.veriserve.co.jp/helloqualityworld/service/qualityforward/#top)に申し込み、試使用を行ってみました。高品質なソフトウェアを迅速に提供していく上で欠かせないCI/CD環境の構築を、QualityForwardと各種ツールとの連携を行うことで実現してみました。QualityForwardをこれから活用してみたいという方の、参考となれば幸いです！
(本記事は、QualityForwardを用いたテスト管理の体験談を投稿しよう！ by QualityForward Advent Calendar 2024への投稿記事になります)

https://qiita.com/advent-calendar/2024/qualityforward02


## QualityForwardの特徴
### テストデータの詳細な分析レポートと可視化
プロジェクト全体の進捗やバグの改善状況、テスト実行ステータスの可視化レポートをリアルタイムで自動生成します。また、「カバレッジパネル」機能を用いることで、テストの定性的な進捗網羅性を把握することも可能です。

### 多様なツールとのシームレスな連携
QualityForwardはWeb APIをサポートしているため、以下のメリットがあります。
- ツール間の統合や連携が容易
- データの自動収集や登録が可能
- 柔軟なシステム拡張が可能

[**QualityForward　APIリファレンス**](https://qualityforward.github.io/api-spec/) が整っているので、こちらを参考にテストデータの収集や登録を行うことができます。
また、[サンプルスクリプト](https://qualityforward.zendesk.com/hc/ja/sections/7241610097945)も充実しているので、システムを構築する際に参考になります。


### 価格とプラン
料金体系とそれぞれのプランは以下の通りとなっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/eb38ea92-e554-c1bf-900d-a17bc67b6d88.png)

フリープランの場合Web APIは利用できないようですが、その他の機能に関しては基本的に使用できるようなので、開発のイメージを持つには十分だと思います。
**※今回は、Veriserveご担当者様にご相談させていただき、1か月ビジネスプランの試使用をさせていただきました。ありがとうございます。**


## CI/CD環境のシステムアーキテクチャ
それでは実際に、Quarity Forwardを利用したCI/CD環境を構築していきます。
今回は、自動テストフレームワークのCypressと、CIツールであるGitHub Actionsを利用して、Quarity Forwardと連携することで、テスト自動化環境を構築したいと思います。また、Quarity Forwardの機能であるBTS連携(JIRA)を利用して、一連の開発フローを実現してみました。

構築システムの概略図は以下となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/0b3671e1-b03b-1128-2b13-577463c92f0e.png)

開発サイクルの流れとしては、以下の通りとなります。
1. **ソフトウェア開発者がローカル環境で開発**
    ・JIRAチケットでアサインされている業務を確認
    ・ローカル環境でコード開発
    ・cypressを利用して動作確認
    ・ローカル環境で動作確認後、GitHubへコードをpush
    <br/>
2. **GitHub Actionsによる自動テスト**
    ・コードのpushを契機に自動workflowがトリガーされる
    ・cypressの自動テストを実行
    ・テスト結果をQualityForwardへUpload
    <br/>
3. **QualityForwardでテスト結果および進捗等確認**
    ・PMやQA担当がQualityForwardの表示を確認し、進捗や課題を抽出
    ・ソフトウェア開発者はテスト結果から解決すべきバグが見つかった場合は、JIRAチケットを起票する
    <br/>
4. **JIRAチケット管理**
    ・ソフトウェア開発者はJIRAチケットから、修正すべきバグ等を確認する
    <br/>

## GitHubの設定
GitHub Actionsを利用して、Cypressを利用した自動テスト環境を構築します。
テスト結果をQualityForwardと連携する箇所のworkflowに関しては、過去記事[【QualityForward】自動テスト安定性レポートを簡単に活用できるGitHub Actionsを作りました](https://qiita.com/tru-y/items/c70c3574232820e558ed)で紹介されているactionsを参考に一部修正して利用しました。

テスト実行プロジェクトとしては、[Cypressのサンプルプロジェクト](https://github.com/gurock/automation-frameworks-integration/tree/main/samples/javascript/cypress)を用いました。
フォルダ構成は、以下の通りです。
```bash
.
├── README.md
├── .github
│   └── workflows
│       └── quality-forward.yaml                      ##Workflow定義ファイル
├── scripts
│   └── xml_parser.py                                 ##xmlファイル修正用スクリプト
├── cypress　　　　　 　　　　　　　　　　　　　　　      ##Cypressのサンプルプロジェクト
│   ├── e2e
│   │   └── todo_app
│   │       ├── todo_interactions.cy.js
│   │       └── todo_smoke.cy.js
│   ├── fixtures
│   │   ├── example.json
│   │   ├── profile.json
│   │   └── users.json
│   ├── plugins
│   │   └── index.js
│   └── support
│       ├── commands.js
│       ├── e2e.js
│       └── index.js
├── cypress.config.js
├── package-lock.json
└── package.json
```

#### Workflow定義ファイル
GitHub Actionsの定義ファイルは以下の通りに設定します。
```yml:quality-forward.yaml
name: quality-forward-test
on: [workflow_dispatch,push]
jobs:
  execute-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Cypress run
        id: cypress
        uses: cypress-io/github-action@v4
        with:
          command: npx cypress run --reporter junit --reporter-options mochaFile=reports/TEST-[hash].xml
      - name: Python setup
        if: always()
        uses: actions/setup-python@v3
        with:
          python-version: '3.x'
      - name: Install dependencies
        if: always()
        run: |
          python -m pip install --upgrade pip
          pip install junitparser
      - name: Merge Test results
        if: always()
        run: |
          junitparser merge --glob "reports/TEST-*" "reports/junit-report.xml"
          ${{ github.workspace }}/scripts/xml_parser.py
      - name: link automated test results
        if: always() 
        uses: tomyamkung/test-stability-report-sync@feature/add_info_url
        with:
          api-key: ${{ secrets.QUALITY_FORWARD_API_KEY }}
          file-path: reports/junit-report-updated.xml
          test-framework: junit
          auto_test_suite_external_key: sample1-1
          info_url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: video-file
          path: ${{ github.workspace }}/cypress/videos/**

```

各Stepの概要です。
1. **チェックアウト**
    ・actions/checkout@v3を利用して、runnerサーバー上にリポジトリのコードをチェックアウトする
    <br/>
2. **自動テストの実行**
    ・cypress-io/github-action@v4を利用して、リポジトリにある自動テストを実行する
    ・自動テストが実行されるとJUnitレポートが生成される
    <br/>
3. **Pythonのセットアップ**
    ・actions/setup-python@v3を利用して、junitparserなどのツール実行に必要なPythonをrunnerサーバー上にインストールする
    <br/>
4. **必要なモジュールのインストール**
    ・junitparserのモジュールをインストール
    <br/>
5. **テスト結果のマージおよびフォーマット**
    ・自動テストの結果ファイルを一つのxmlファイルにマージする
    ・xmlファイルの中身を、QualityForwardへ取り込めるように修正する
    <br/>
6. **自動テスト結果をQuarityForwardにアップロードする**
    ・QualityForward/test-stability-report-sync@v1.2.0を利用して、自動テスト結果をQualityForwardへアップロードする。
    <br/>

※Workflowで用いる"**QUALITY_FORWARD_API_KEY**"に関しては、[Quality ForwardのAPIキー発行手順](https://qualityforward.github.io/api-spec/#section/API/API)をもとに発行したAPIキーを、[GitHubシークレット](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions)に登録して利用しています。

今回実行したcypressの自動テストのテスト結果では、冗長な<testsuite>タグが含まれていたため、以下pythonスクリプトを実行することで修正しています。

```python:xml_parser.py
#!/usr/bin/env python3

import xml.etree.ElementTree as ET
import os

def clean_xml(input_file, output_file):
    # Resolve relative paths
    input_path = os.path.abspath(input_file)
    output_path = os.path.abspath(output_file)

    # Parse the input XML
    tree = ET.parse(input_path)
    root = tree.getroot()

    # Find and remove redundant testsuite entries with "Root Suite"
    for testsuite in root.findall("testsuite"):
        if testsuite.get("name") == "Root Suite" and int(testsuite.get("tests", 0)) == 0:
            root.remove(testsuite)

    # Write the modified XML to the output file
    tree.write(output_path, encoding="utf-8", xml_declaration=True)

# Usage example
input_file = "./reports/junit-report.xml" 
output_file = "./reports/junit-report-updated.xml"
clean_xml(input_file, output_file)

```

修正後の自動テスト結果のxmlファイルは以下のようになります。

```xml:junit-report.xml
<?xml version='2.0' encoding='utf-8'?>
<testsuites tests="4" failures="1" errors="0" skipped="0" time="1.81"><testsuite name="[smoke] Todo app list" timestamp="2024-12-28T12:08:10" tests="1" time="0.641" failures="0" errors="0" skipped="0">
    <testcase name="[smoke] Todo app list displays two todo items by default" time="0.641" classname="displays two todo items by default">
    </testcase>
  </testsuite>
<testsuite name="[interactions] Todo app list with a checked task" timestamp="2024-12-28T12:07:57" tests="3" time="1.169" failures="1" errors="0" skipped="0">
    <testcase name="[interactions] Todo app list with a checked task can filter for uncompleted tasks" time="0.801" classname="can filter for uncompleted tasks">
    </testcase>
    <testcase name="[interactions] Todo app list with a checked task can filter for completed tasks" time="0.368" classname="can filter for completed tasks">
    </testcase>
    <testcase name="[interactions] Todo app list with a checked task can delete all completed tasks" time="0.000" classname="can delete all completed tasks">
      <failure message="Timed out retrying after 4000ms: Not enough elements found. Found '1', expected '2'." type="AssertionError">AssertionError: Timed out retrying after 4000ms: Not enough elements found. Found '1', expected '2'.
    at Context.eval (webpack:///./cypress/e2e/todo_app/todo_interactions.cy.js:34:7)

      + expected - actual

      -1
      +2
      </failure>
    </testcase>
  </testsuite>
</testsuites>

```

#### test-stability-report-sync actionの一部修正
[APIリファレンス](https://qualityforward.github.io/api-spec/#tag/AutoTestResult/operation/PostAutoTestResults)の自動テスト結果入力によると、登録できる項目にinfo_urlがありました。この項目には、当該テスト結果に関する詳細情報が記載されたWebページのURLを格納することができます。
しかし、過去記事[【QualityForward】自動テスト安定性レポートを簡単に活用できるGitHub Actionsを作りました](https://qiita.com/tru-y/items/c70c3574232820e558ed)で紹介されている[QualityForward/test-stability-report-sync](https://github.com/QualityForward/test-stability-report-sync/tree/v1.2.0) actionでは、まだinfo_urlに関しては入力のサポートがされていなかったため、一部を修正することで、info_urlにURLを代入することを可能にしました。
今回は、このinfo_urlにGithub Actionsの実行結果のURLを格納しました。そのURLをクリックすることで、cypress自動テストの結果として出力されるvideo logをダウンロードすることができます。

## 自動テストの実行とQuality Forwardへの連携
### GitHub Actions workflowの実行
Workflowを実行すると、Cypress runでエラーが生じておりますが、これはテストケースの中でfailしているものがあることを意味しています。
今回の場合ですと、全4つのテストのうち、"todo_interactions.cy.js"の一つのテストが失敗していることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/78933654-ab6c-d17c-f6b0-532b19d6a438.png)

自動テストがfailしても、テスト結果そのものは毎回Quality Forwardと連携できるようにしています。今回利用した、[QualityForward/test-stability-report-sync@v1.2.0](https://github.com/QualityForward/test-stability-report-sync/tree/v1.2.0)では、Quality Forwardとの連携が成功すると、「The integration of automated test results has been completed.」と表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/7ae3c353-108a-6915-130c-887cb589238b.png)

### QualityForwardの自動テスト安定性レポート
QualityForwardに自動テスト結果を連携させると、自動テスト安定性レポートによる可視化をすることができます。
自動テスト安定性レポートは、ユーザーが行ったGitHubActionsやJenkinsなどのCIツールと、テストフレームワークを連携させて実行した自動テストに対して、時系列で結果や実行時間を表示することで、自動テストをサポートする機能です。自動テストは、開発スピードの向上やテストの効率性の向上などに寄与する重要な機能ですが、メンテナンスをしっかりと行っていく必要があります。
自動テスト安定性レポートは、自動テストの活用がしやすい環境をユーザーに提供します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/f06573bd-1148-b739-4ae6-679615da29aa.png)
(参照：https://qualityforward.zendesk.com/hc/article_attachments/34661244680857)

Quality Forwardへの連携が成功したら、実際にQuality Forwardで結果を見てみます。

<自動テスト全体>
自動テスト全体の結果分布および、自動テストスイートごとの失敗数分布が円グラフで表現されていることがわかります。また、自動テストスイートと実行環境ごとの実行回数、累積成功・失敗率の一覧も表示されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/94dac466-0940-d4b4-47e9-42b26d0402c4.png)

<自動テストスイート詳細>
自動テストスイートのチャートを押下すると、自動テスト結果分布が円グラフで出力されます。また、テストケースごとに時系列でテスト結果を表示することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/0d1bf791-2999-9424-1e53-d0d360c0a9f7.png)

さらに、それぞれの結果「Pass」や「Fail」はリンクとなっていて、info_urlに設定したURLを押下することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/e353c45c-0e51-8772-6ef1-73e6583827e1.png)

クリックすると、Github Actionsのログ結果のページを表示することができます。
ここで、cypressの自動テストによるvideo logのファイルをダウンロードして確認も可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/7feae00a-a5ab-e2fa-9ce5-7b17371c7765.png)

### テスト自動化状況レポート

テスト自動化連携機能の一つに、テスト自動化状況レポート機能があります。
これは、テストケースごとに自動化検討状況を管理できる機能で、どのテストケースの自動化が遅れているかなどを視覚的に表示することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/d0e69572-9b98-cd14-d3cd-4b8ef54fc149.png)
(参照：https://qualityforward.zendesk.com/hc/article_attachments/34661244680857)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/006837c6-3e13-e46f-2c00-9f9394591c85.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/17149b2b-409a-e20e-5d8d-a7d65940b50a.png)


## Attlassian JIRAとのBTS連携
続いて、QuarityForwardのBTS連携機能を利用し、バグ管理ツールのJIRAと連携したいと思います。

### Jira Cloud APIトークンの作成
まず、Jira Cloudインスタンスにログインし、画面右上の歯車のアイコンをクリックし、[**Atlassian アカウント設定**]を選択します。[**Security**]メニューから、[**APIトークンの作成と管理**]を選択し、[**APIトークンを作成する**]をクリックします。任意のラベル名を指定してAPIトークンを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/38db081f-c88e-e7bf-e66a-c2aaa34cdd2a.png)

### QualityForward側設定
- テストフェーズ一覧のテストフェーズ設定から、連携するBTSで「JIRA」を選択するとJIRAに連携するための設定項目が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/8d00acf1-7710-bd4e-11a8-93161a3a5243.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/e9315183-bb51-4a16-416c-8ef4e87c7f29.png)

- ユーザ名にはメールアドレスを、パスワードには[Jira Cloud APIトークンの作成](#Jira-Cloud-APIトークンの作成)で生成したAPIトークンを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/2a45ef1c-f5d5-c667-6a0e-2f3236573aa0.png)

- JIRAのURLには、連携するJIRAのURLを記載します。
- JIRAから各種データを取得し、QualityForward側に表示させるための設定を行います。JQLという、JIRA専用のクエリ言語を指定することで、取得するプロジェクトや課題のタイプを絞りこむことができます。今回は、project="KAN"というプロジェクトにします。
- JIRAの最近のインシデント取得用JQLを設定します。今回は未設定ですが、未設定の場合は、バグ曲線、グラフデータ取得用JQLに設定したJQLから情報を取得します。
- 新規チケットの作成画面のURLには、QualityForward側から新規チケットを作成するためのJIRA URLを指定します。この際に、pidの値によりプロジェクトが、issuetypeの値によりチケットの課題タイプが決定されます。私のJIRAのサンプルプロジェクトでは、"KAN"プロジェクトに相当するpidが"10000"、タスクタイプのチケットに相当するissuetypeが"10001"でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/3738482f-5b07-3962-10dc-0f8a2e8f3437.png)

### JIRAとの疎通確認
設定が完了したら、"JIRAとの疎通確認"ボタンを押下し疎通できているか確認します。問題なければ、結果が"〇"となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/00cea7e0-bad7-d1b6-6603-b462262cb8fc.png)

### BTS連携の確認
BTS連携の設定が完了すると、JIRAプロジェクトの状況がQualityForward側にも反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/0a493364-5751-d40f-613d-e2e4f5235f98.png)
例えば、JIRA側のタスクチケットが上図のような状態であった場合、JIRAからタスクの状況などのデータが収集され、QualityForward側のレポートは下図のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/57c0a73e-ebec-269b-d9a8-3d7fbbbc0f1b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/7b942b8e-7f38-0b79-d684-1f5b8dd96540.png)

このようにして、QualityForwardとBTS(JIRA)が連携することができ、バグの状況などを可視化できます。

### QualityForwardからJIRAチケットの発行
さらに、テストサイクルから閲覧で結果を表示した際に、BTSに起票というボタンを押下すると、自動でJIRAチケットの起票画面に遷移し、チケットを起票することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/12fb9e19-0afd-0907-ccd0-4743ccae8220.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378598/c8b3bfc1-64a0-668d-7ede-2dcfd604c2a7.png)

## Quality Forwardを実際に使ってみた所感
QualityForwardを使用して、簡易的なCI/CD環境を構築してみた所感としては、以下の3点です。

1. 分析レポートの視える化の充実
2. WebAPI機能により、各ツールとの連携が容易
3. ユーザーフレンドリーな操作性

### 1. 分析レポートの視える化の充実
テスト結果や品質レポートがリアルタイムで視覚的に表示される点は特筆すべきポイントです。

- ダッシュボードの充実度: プロジェクトのテスト進捗、エラーの傾向、品質指標（カバレッジ率や不具合数など）がグラフやチャートで一目で確認できます。特に、時系列でデータを追える機能は問題発生箇所や改善の効果を素早く分析するのに役立てると思います。
- カスタマイズ可能なレポート: チームごとのニーズに合わせてダッシュボードを自由にカスタマイズできるため、マネージャー向けとエンジニア向けの異なる視点で情報を整理することも可能だと思います。これにより、各ステークホルダーが効率的に状況を把握できるようになります。

### 2. WebAPI機能により、各ツールとの連携が容易
開発環境に依存せず、多種多様なツールと簡単に連携できるWebAPI機能は、運用の効率化に大きく貢献しました。

- 統合の柔軟性: JenkinsやGitHub ActionsなどのCIツールから、JIRAやRedmineなどのBTSツールまで、APIを通じて容易に統合できました。これにより、CI/CD環境をシームレスに構築が可能となり、自動化レベルをさらに向上させることができます。

### 3. ユーザーフレンドリーな操作性
操作性が非常に優れており、新しいメンバーでも短時間で環境を整備して使いこなせることが期待できます。

- 直感的なインターフェース: ユーザーインターフェースが洗練されており、複雑な設定なしに必要な機能へアクセスできました。特にフィルタリング機能や検索機能は非常に実用的で、特定のテストケースやエラーをすぐに確認できます。
- 充実したドキュメント: 初心者向けのガイドから高度なAPI使用例まで幅広くカバーされており、学習コストが低く抑えられていると感じます。

## まとめ
今回、「**Quality Forward**」の [**フリープラン**](https://www.veriserve.co.jp/helloqualityworld/service/qualityforward/#top)に申し込み、QualityForwardの機能を横断的に使用してみました。WebAPIを利用した自動テストとの連携やBTSツール(JIRA)との連携などを体感することができました。
実際にQualityForwardを利用してCI/CD環境を構築し、高品質なソフトウェアを素早くリリースしたいという方々は、ぜひQualityForwardの無料プランを利用してみて、活用のイメージをしていただけたらと思います。
本記事が、皆様の参考になれば幸いです！