はじめに
ソフトウェアの品質を保証していくために、テストの自動化と管理は非常に重要な観点です。その中で、テストケースやテスト結果をエクセルやスプレッドシートでまとめて管理されている方も多いのではないでしょうか。GoogleAppScriptなどと連携させることで、テスト管理への対応が柔軟にできるようになってきている一方で、不便な部分も依然あるように感じます。
今回、テクマトリックス社が提供するテスト管理ツールである「Test Rail」の 30日間 無料トライアル(クラウド版)に申し込み、試使用を行ってみました。テスト自動化やテスト管理の観点で、スプレッドシートなどの表計算ソフトと比較したメリデメなどを紹介していきたいと思います。また、テストのサンプルデータを用いて自動化ツールとの連携の仕方に関しても紹介できればと思いますので、参考にしていただければと思います。
(本記事は、Qiita Engineer Festa 2024「そうだ! TestRailを使ってみよう!!」への投稿記事になります)
TestRailの特徴
多様なツールとシームレスな連携が可能
TestRailの特徴としては、JIRAやRedmineなどの課題管理ツール、GitHubやBitBucketなどのバージョン管理ツール、JenkinsやGitHub ActionsなどのCI/CDツールなど様々なツールとの連携をサポートしています。これにより各プロジェクトのニーズに合わせたテスト管理を柔軟に実現することができます。
TestRail CLIのサポート
TestRailはCLI(コマンドラインインターフェース)をサポートしており、JUnit形式のXMLファイルをCLI実行することで、ファイルを解析してTestRailにアップロードすることができます。そのため、PytestやCypressなどの多くの自動化フレームワークに対応しており、様々なプロジェクトで応用が可能です。また、JenkinsやGithub ActionsなどのCIツールとの連携も容易にでき、テスト実行から結果表示までの自動化を促進します。CLIを活用した自動化システムの構築を今回行っていきたいと思います。
TestRail CLIの仕様に関しては、公式ページを参考にしてください。
テスト自動化システムの構築
百聞は一見に如かずということで、「Test Rail」の 30日間 無料トライアル(クラウド版)で、より実用的なシステムの構築を行ってみました。
今回は、E2EテストフレームワークのCypressと、CIツールであるGithub Actionsを利用してTestRailと連携することで、テスト自動化システムを構築してみたいと思います。さらに、課題管理ツールのJIRAとも連携させることで、一連の開発サイクルを実現してみました。
構築システムの概略図は以下となります。
開発サイクルの流れとしては、以下の通りとなります。
-
ソフトウェア開発者がローカル環境で開発
・JIRAチケットでアサインされている業務を確認
・ローカル環境でコード開発(アプローチとしては、コードファーストによるアプローチになります。)
・cypressを利用して動作確認
・ローカル環境で動作確認後、GitHubへコードをpush
-
GitHub Actionsによる自動テスト
・コードのpushを契機に自動workflowがトリガーされる
・cypressの自動テストを実行
・テスト結果をTestRailへUpload
-
TestRailでテスト結果および進捗等確認
・PMやQA担当がTestRailの表示を確認し、進捗や課題を抽出
・ソフトウェア開発者はテスト結果から解決すべきバグが見つかった場合は、JIRAチケットを起票する
-
JIRAチケット管理
・ソフトウェア開発者はJIRAチケットから、修正すべきバグ等を確認する
それでは、システムを構築するための具体的な作業手順を記載いたします。
TestRailの設定
プロジェクトの作成
まずは、プロジェクトを作成します。ダッシュボード画面の[プロジェクトの追加]を選択し、"Sample Project"という名前で今回はプロジェクトを作成します。
TestRailインスタンスの設定
GitHub ActionsでTestRail CLIを使用するために、TestRailインスタンスでAPIの有効化を行う必要があります。これは、TestRail CLIを実行すると、内部でTestRailのAPIを叩いているためです。設定としては、[管理]>[サイト設定]から[API]タブを選択し、[APIの有効化]にチェックを入れて、設定を保存します。
カスタムフィールド設定
続いて、自動テストケースのコードと実際のTestRailのケースをマッピングするためのカスタムフィールドを作成します。[管理]>[カスタマイズ]から[フィールドの追加]を選択します。システム名を"automation_id"、タイプを"string"として設定してフィールドを保存します。
GitHubの設定
続いて、GitHubの設定です。
プロジェクトとしては、Cypressのサンプルプロジェクトを用いました。
フォルダ構成は、以下の通りです。
.
├── README.md
├── .github
│ └── workflows
│ └── testrail.yaml ##Workflow定義ファイル
├── 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
├── scripts
│ └── xml_parser.py ##テスト結果ファイル修正スクリプト
└── trcli-config.yml
Workflow定義ファイル
GitHub Actionsの定義ファイルは以下の通りに設定します。
name: cypress-tests
on: [workflow_dispatch,push]
jobs:
execute-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- 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: TestRail CLI upload results
if: always()
run: |
pip install trcli
junitparser merge --glob "reports/TEST-*" "reports/junit-report.xml"
sed -i 's/([^)]*)//' "reports/junit-report.xml"
${{ github.workspace }}/scripts/xml_parser.py
trcli -y \
-h https://${{ secrets.TESTRAIL_INSTANCE }}.tmxtestrail.com/ \
--project "Sample Project" \
-u ${{ secrets.TESTRAIL_USER_EMAIL }} \
-p ${{ secrets.TESTRAIL_PASSWORD }} \
parse_junit \
--title "Automated Tests from GitHub workflow" \
--run-description ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} \
-f "reports/junit-report-updated.xml"
各Stepの概要です。
-
チェックアウト
・actions/checkout@v3を利用して、runnerサーバー上にコードをチェックアウトする
-
自動テストの実行
・cypress-io/github-action@v4を利用して、リポジトリにある自動テストを実行する
・自動テストが実行されるとJUnitレポートが生成される
-
Pythonのセットアップ
・actions/setup-python@v3を利用して、TestRail CLIの実行に必要なPythonをrunnerサーバー上にインストールする
-
TestRail CLIのインストールとテスト結果のアップロード
・Test CLIをpipでインストールする
・junitparserを用いて、複数のJUnit XMLレポートを一つのレポートに統合する
・XMLレポートに含まれる"(**)"を、sedで取り除く(TestRailへのUpload時にエラーになります)
・XMLレポートを操作して、テスト実行時の録画ファイルを埋め込んで、TestRailに同時にUploadできるようにする(次のセクションで説明します)
・TestRail CLIを用いて、テスト結果をTestRailにアップロードする
※Workflowで用いる、"TESTRAIL_INSTANCE"、"TESTRAIL_USER_EMAIL"、"TESTRAIL_PASSWORD"に関しては、固有の情報をGitHubシークレットに登録して利用しています。
テスト結果ファイル修正スクリプト
テスト結果をアップロードする際に、実行時の録画ファイルも同時にUploadしたいというユースケースはあると思います。Test CLIではそのようなニーズをサポートしており、XMLファイルに"testrail_attachment"のプロパティを追加することで、添付ファイルを追加することが可能となります。
その他のサポートしているプロパティに関しては、JUnitからTestRailへのマッピングを参照してください。
すべての結果のフィールドに同じ値を追加する場合、--result-fields を 1 回または複数回使用して、コマンド ラインで直接値を渡すことができます。
公式マニュアルから、Test CLIコマンドで、--result-fieldsオプションを使用するとコマンドから値を渡すことができると記載がありましたが、TestSuiteごとに値を変更する場合は、今のところXMLレポートを直接修正する必要がありそうだったので、以下pythonスクリプトを作成しました。
#!/usr/bin/env python3
from xml.dom import minidom
import xml.etree.ElementTree as ET
def add_attachment():
tree = ET.parse('reports/junit-report.xml')
root = tree.getroot()
# Iterate over the 'testcase' elements to find the specific one
for testsuite in root.findall('testsuite'):
for testcase in testsuite.findall('testcase'):
# Check for the specific 'testcase' we want to add the attachment to
if testcase.attrib['name'].startswith("[smoke]"):
# Create the new 'properties' element and its child 'property' element
properties = ET.Element('properties')
property_element = ET.Element('property')
property_element.set('name', 'testrail_attachment')
property_element.set('value', './cypress/videos/todo_smoke.cy.js.mp4')
# Append the new 'property' element to the 'properties' element
properties.append(property_element)
# Append the 'properties' element to the 'testcase' element
testcase.append(properties)
if testcase.attrib['name'].startswith("[interactions]"):
# Create the new 'properties' element and its child 'property' element
properties = ET.Element('properties')
property_element = ET.Element('property')
property_element.set('name', 'testrail_attachment')
property_element.set('value', './cypress/videos/todo_interactions.cy.js.mp4')
# Append the new 'property' element to the 'properties' element
properties.append(property_element)
# Append the 'properties' element to the 'testcase' element
testcase.append(properties)
# Write the updated XML back to the file
tree.write('reports/junit-report-updated.xml', encoding='utf-8', xml_declaration=True)
add_attachment()
このスクリプトを実行することで、TestSuiteごとに
- name="testrail_attachment" value="todo_interactions.cy.js.mp4"
- name="testrail_attachment" value="todo_smoke.cy.js.mp4
のpropertyを挿入するこができます。
<?xml version='1.0' encoding='utf-8'?>
<testsuites tests="4" failures="1" errors="0" skipped="0" time="1.593"><testsuite name="Root Suite" timestamp="2024-07-07T01:24:58" tests="0" file="cypress/e2e/todo_app/todo_interactions.cy.js" time="0" failures="0" errors="0" skipped="0">
</testsuite>
<testsuite name="[interactions] Todo app list with a checked task" timestamp="2024-07-07T01:24:58" tests="3" time="1.09" failures="1" errors="0" skipped="0">
<testcase name="[interactions] Todo app list with a checked task can filter for uncompleted tasks" time="0.750" classname="can filter for uncompleted tasks">
</testcase>
<testcase name="[interactions] Todo app list with a checked task can filter for completed tasks" time="0.340" 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
+ expected - actual
-1
+2
</failure>
</testcase>
</testsuite>
<testsuite name="Root Suite" timestamp="2024-07-07T01:25:11" tests="0" file="cypress/e2e/todo_app/todo_smoke.cy.js" time="0" failures="0" errors="0" skipped="0">
</testsuite>
<testsuite name="[smoke] Todo app list" timestamp="2024-07-07T01:25:11" tests="1" time="0.503" failures="0" errors="0" skipped="0">
<testcase name="[smoke] Todo app list displays two todo items by default" time="0.503" classname="displays two todo items by default">
</testcase>
</testsuite>
</testsuites>
<?xml version='1.0' encoding='utf-8'?>
<testsuites tests="4" failures="1" errors="0" skipped="0" time="1.593"><testsuite name="Root Suite" timestamp="2024-07-07T01:24:58" tests="0" file="cypress/e2e/todo_app/todo_interactions.cy.js" time="0" failures="0" errors="0" skipped="0">
</testsuite>
<testsuite name="[interactions] Todo app list with a checked task" timestamp="2024-07-07T01:24:58" tests="3" time="1.09" failures="1" errors="0" skipped="0">
<testcase name="[interactions] Todo app list with a checked task can filter for uncompleted tasks" time="0.750" classname="can filter for uncompleted tasks">
<properties><property name="testrail_attachment" value="todo_interactions.cy.js.mp4" /></properties></testcase>
<testcase name="[interactions] Todo app list with a checked task can filter for completed tasks" time="0.340" classname="can filter for completed tasks">
<properties><property name="testrail_attachment" value="todo_interactions.cy.js.mp4" /></properties></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
+ expected - actual
-1
+2
</failure>
<properties><property name="testrail_attachment" value="todo_interactions.cy.js.mp4" /></properties></testcase>
</testsuite>
<testsuite name="Root Suite" timestamp="2024-07-07T01:25:11" tests="0" file="cypress/e2e/todo_app/todo_smoke.cy.js" time="0" failures="0" errors="0" skipped="0">
</testsuite>
<testsuite name="[smoke] Todo app list" timestamp="2024-07-07T01:25:11" tests="1" time="0.503" failures="0" errors="0" skipped="0">
<testcase name="[smoke] Todo app list displays two todo items by default" time="0.503" classname="displays two todo items by default">
<properties><property name="testrail_attachment" value="todo_smoke.cy.js.mp4" /></properties></testcase>
</testsuite>
</testsuites>
Atlassian Jira統合
最後に、Atlassian JiraとTestRailとの連携になります。
注意
今回使用しているTestRail Version8.0.1では、JIRA統合の際にAtlassian APIトークンに関連するバグが発見されています。そのため、ユーザー変数を利用したトークン管理を行うことで暫定的に回避いたしました。詳細は以下をご覧ください。
Atlassian APIトークンのご利用に関するお知らせ
Jira Cloud APIトークンの作成
まず、Jira Cloudインスタンスにログインし、画面右上の歯車のアイコンをクリックし、[Atlassian アカウント設定]を選択します。[Security]メニューから、[APIトークンの作成と管理]を選択し、[APIトークンを作成する]をクリックします。任意のラベル名を指定してAPIトークンを作成します。
Test Rail側設定
[管理]>[統合]をタブから、JIRAの[統合の設定]を選択します。JIRAのアドレスや電子メールアドレスを入力し、JIRA統合の有効化をクリックします。
前述の、Attlassian APIトークンのバグの暫定回避策として、ユーザー変数タブのjira_tokenのタイプを文字列にして、フォールバックの中に、生成したJira Cloud APIトークンを入力し設定の保存を行います。
以上で、テスト自動化システムの構築は完了です。
テスト自動化システムの実行結果
GitHub Actions workflowの実行
ソフトウェア開発者が、コードをGitHubのリポジトリにpushすると自動でワークフローがトリガーされてテストが実行されます。(トリガー条件は、定期実行や手動実行など様々な条件設定が可能です。詳細は、ワークフローをトリガーするイベントをご覧ください。)
ワークフローの詳細を見ると、ステップごとの実行ログを確認できます。このサンプルでは、Cypress runの実行で失敗していることがわかります。
全4つのテストのうち、"todo_interactions.cy.js"の一つのテストが失敗していることがわかります。
TestRailの結果の確認
TestRailを開き、Sample Projectの"テストランと結果"タブをクリックすると、先ほどGitHub Actionsで実行したWorkflowの結果が反映されていることが確認できます。
結果を選択すると、テストのサマリが表示されます。今回の場合ですと、4つのテストのうち3つのテストがパスし、1つのテストだけ失敗していることがわかります。
今回TestRail CLIの機能を活用して、テスト実行時の録画ファイルがTestRailにアップロードされていることです。この機能を活用することで、テスト結果をより充実させることが可能となります。
バグチケットの起票
失敗したテストに関しては、バグチケットを起票したいと思います。
失敗したテストを選択し、"結果の追加"を押すと下図のようなWindowが表示され、ステータスやコメント、アサインなどを記載することができます。
欠陥の横にある"push"をクリックすると、下図のようなWindowが表示され、Issue TypeやPriorityなどを記載することができます。
登録を押すと、JIRA側で自動でバグチケットが作成されたことが確認できます。
開発者はこのバグチケットをもとにコードを修正して、再び自動テストを実行するといったように、開発のサイクルを回すことができます。TestRailが多くのツールとシームレスに連携することが容易であることから、このような開発が可能であるということが確認できました。
レポート機能
TestRailは、レポート機能が充実しているという特徴があります。テストランやマイルストーンなどのサマリーレポートや、テストケースのテスト結果を比較したりすることができるレポート機能もあります。
状況の視える化をすることにより、PMであったりQA担当者がプロジェクトの品質状況を常に監視することができます。開発者は報告のために工数を割かずに済み、自身の開発に専念することが可能となります。
スプレッドシートによるテスト管理との比較
現在私たちの開発チームでは、スプレッドシートを利用したテスト管理を行っています。今回、TestRailを実際に使ってみて、スプレッドシートでテスト管理をする場合とのメリデメをまとめてみました。
TestRail | スプレッドシート | |
---|---|---|
ツール連携 | ◎ | △ |
学習コスト | △ | △ |
結果レポートや表機能 | ◎ | 〇 |
ツール連携
自動化ツールとの連携では、TestRailに軍配が上がると思います。今回利用したGitHubActionsだけではなく、JenkinsやCircleCIなどともシームレスに連携が可能です。TestRail CLIの機能が充実しており、APIもサポートしているので、活用の幅が広がります。
一方でスプレッドシートの管理では、Pythonを利用してデータを操作しており、決まったフォーマットに決まった書き方でテストケースを入力することで、自動で抽出できるようにしています。しかし、フォーマットがずれてしまったりすると、ワークフローですぐにエラーとなってしまいバグ修正を行う必要がでてきます。
自動化スクリプトの修正工数が多くなってしまうことが課題としてあげられます。
その他のチケット管理ツールなどの連携に関しても、TestRailはシームレスな連携が可能であるため、TestRailに軍配があがります。
学習コスト
学習コストに関しては、TestRailもスプレッドシートも大差がないと感じています。
TestRailは多機能をサポートしているため、活用するために多くの機能を仕様書から学ぶ必要がありますが、スプレッドシートも、マクロの活用法であったり、pythonによるデータの反映方法など学ぶことが多いです。
結果レポートや表の機能
TestRailには、充実したレポート機能や、マイルストーンや進捗状況などの表を表示する機能があります。これらは非常に強力である一方で、しっかりと開発者が理解して使用していく必要があります。
レポート機能という面では、スプレッドシートとLucudchartを連携させることで、データを視覚的に表現することが可能で、十分に活用が可能です。ただしこれも一定のメンテナンスが必要だという欠点があります。
まとめ
今回、「Test Rail」の 30日間 無料トライアル(クラウド版)に申し込み、TestRailの機能を横断的に使用してみました。実際にJiraやGitHubActionsなどと連携することで、テスト自動化システムを構築することができ、TestRailの機能を体感することができました。設定方法なども詳細に記載したので、ぜひみなさんの参考になれば幸いです。実際にスプレッドシートなどの表計算ツールから移行をお考えの方は、ぜひ「Test Rail」の 30日間 無料トライアル(クラウド版)を利用して、多くの機能を体験してみることをおすすめします。(デモサイトでは機能が制限されています。)
TestRailの多くの魅力的な機能を活用して、ぜひ生産性を劇的に改善してみましょう!