JUnitテストレポート

GitLab 11.2で導入されました。GitLab Runner 11.2以降が必要です。

概要

CI/CDパイプラインにコードを検証するテストジョブが含まれていることは非常によくあることです。テストが失敗するとパイプラインが失敗したことがユーザーに通知されます。マージリクエストの作業をしている人はジョブのログをチェックし、どこでテストが失敗したかを確認して修正しなければなりません。

JUnitのテストレポートを使用するようにジョブを設定すると、GitLabのマージリクエストにレポートが表示されるので、ログ全体をチェックしなくても簡単かつ迅速に障害を特定できます。

マージリクエストを使用せずに、ジョブのログを検索せずにJUnitの出力を見たい場合は、パイプラインの詳細ビューでJUnitのテストレポートをすべて見ることができます。

ユースケース

次のようなワークフローを考えてみましょう。

  1. あなたのmaster ブランチは磐石で、プロジェクトは GitLab CI/CD を使っていて、パイプラインは何も壊れていないことを示しています。
  2. チームの誰かがマージリクエストを作成して、そのテストが失敗すると、パイプラインには赤いアイコンが表示されます。詳細を調べるには、ジョブのログを調べてテストが失敗した原因を突き止める必要があります。しかし、ログは何千行にもなる場合があります。
  3. JUnitのテストレポートを設定すると、GitLabのマージリクエストに収集されたテスト結果がすぐに表示されます。これにより、ジョブのログを検索する手間を削減できます。
  4. 開発とデバッグのワークフローがより簡単に、より速く、より効率的になります。

どのように動作するか

まず、GitLab Runnerは、すべてのJUnit XMLファイルをアーティファクトとしてGitLabにアップロードします。 次に、マージリクエストにアクセスすると、GitLabはheadブランチとベースブランチのJUnitテストレポートの比較を開始します。

  • ベースブランチはターゲットブランチ(通常はmaster)です。
  • headブランチとは、マージリクエストのソースブランチのことです。

レポートパネルには、失敗したテストの数、エラーの数、修正されたテストの数を示すサマリーがあります。ベースブランチのデータが利用できず比較ができない場合は、パネルにはheadブランチで失敗したテストのリストが表示されます。

表示される結果は4つに分類されます。

  1. 新しく失敗したテスト: ベースブランチでは成功し、headブランチでは失敗したテストケース
  2. 新たに発生したエラー: ベースブランチでは成功し、headブランチではエラーにより失敗したテストケース
  3. 元から発生していた失敗: ベースブランチとheadブランチの両方で失敗したテストケース
  4. 解決された失敗: ベースブランチで失敗し、headブランチで成功したテストケース

パネル内の各エントリには、上のリストからテスト名とそのタイプが表示されます。テスト名をクリックすると、実行時間とエラー出力の詳細が表示されるモーダルウィンドウが開きます。

Test Reports Widget

設定方法

注意:JUnit テストでサポートされている言語の一覧は、Wikipedia の記事を参照ください。

マージリクエストで JUnit レポートを有効にするには、.gitlab-ci.ymlartifacts:reports:junitを追加し、生成されるテストレポートのパスを指定する必要があります。レポートは.xml ファイルでなければなりません。そうでない場合、GitLab はError 500 を返します

以下の例では、test ステージのジョブが実行され、GitLab が各ジョブから JUnit テストレポートを収集します。各ジョブが実行された後、XML レポートがアーティファクトとして GitLab に保存され、その結果がマージリクエストウィジェットに表示されます。

注意:JUnit の出力ファイルをブラウズする機能も必要な場合はartifacts:pathsキーワードを含めます。 この例を以下の Ruby の例に示します。

Rubyの例

次のジョブを.gitlab-ci.ymlで使用してください。これには、JUnit 出力ファイルへのリンクを提供するartifacts:paths キーワードが含まれています。

## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report with rspec
ruby:
  stage: test
  script:
    - bundle install
    - bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
  artifacts:
    paths:
      - rspec.xml
    reports:
      junit: rspec.xml

Goの例

次のジョブを.gitlab-ci.ymlで使用します。また、-set-exit-codeを使用するようにしてください。そうしないと、テストが失敗しても、パイプラインは成功とマークされます:

## Use https://github.com/jstemmer/go-junit-report to generate a JUnit report with go
golang:
  stage: test
  script:
    - go get -u github.com/jstemmer/go-junit-report
    - go test -v 2>&1 | go-junit-report -set-exit-code > report.xml
  artifacts:
    reports:
      junit: report.xml

Javaの例

Javaには、JUnitレポートを作成できるツールがいくつかあります。

Gradle

以下の例では、gradle テスト・レポートの生成に使用されます。複数のテスト・タスクが定義されている gradle場合、build/test-results/の下に複数のディレクトリが生成されます。その場合、次のパスを定義することで、グロブ・マッチングを活用できます:build/test-results/test/**/TEST-*.xml

java:
  stage: test
  script:
    - gradle test
  artifacts:
    reports:
      junit: build/test-results/test/**/TEST-*.xml

Maven

SurefireおよびFailsafeテストレポーターの解析には、.gitlab-ci.ymlの以下のジョブを使用します:

java:
  stage: test
  script:
    - mvn verify
  artifacts:
    reports:
      junit:
        - target/surefire-reports/TEST-*.xml
        - target/failsafe-reports/TEST-*.xml

Python example

この例では、--junitxml=report.xml フラグを付けた pytest を使用して、JUnit 用の出力をフォーマットしています:

pytest:
  stage: test
  script:
    - pytest --junitxml=report.xml
  artifacts:
    reports:
      junit: report.xml

C/C++の例

C/C++でJUnitレポートを作成できるツールがいくつかあります。

GoogleTest

以下の例では、gtest を使用してテスト・レポートを生成しています。異なるアーキテクチャー(x86x64arm)用に複数の gtest 実行可能ファイルが作成されている場合は、一意のファイル名を指定して各テストを実行する必要があります。 その後、結果が集計されます。

cpp:
  stage: test
  script:
    - gtest.exe --gtest_output="xml:report.xml"
  artifacts:
    reports:
      junit: report.xml

CUnit

CUnit はCUnitCI.h マクロを使用して実行すると、JUnit XML レポートを自動的に作成できます:

cunit:
  stage: test
  script:
    - ./my-cunit-test
  artifacts:
    reports:
      junit: ./my-cunit-test.xml

.NETの例

JunitXML.TestLoggerNuGetパッケージは、.Net Frameworkと.Net Coreアプリケーション用のテストレポートを生成できます。以下の例では、リポジトリのルートフォルダにソリューションがあり、サブフォルダに1つ以上のプロジェクトファイルがあることを想定しています。テストプロジェクトごとに1つの結果ファイルが生成され、各ファイルは新しいアーティファクトフォルダに配置されます。この例には、オプションの書式設定引数が含まれており、テストウィジェット内のテストデータの可読性を向上させます。完全な.Net Coreのサンプルが用意されています

## Source code and documentation are here: https://github.com/spekt/junit.testlogger/

Test:
  stage: test
  script:
    - 'dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=..\artifacts\{assembly}-test-result.xml;MethodFormat=Class;FailureBodyFormat=Verbose"'
  artifacts:
    when: always
    paths:
      - ./**/*test-result.xml
    reports:
      junit:
        - ./**/*test-result.xml

GitLabでJUnitのテストレポートを表示する

  • GitLab 12.5で導入されました
  • フィーチャーフラグで有効・無効を切り替えることができ、デフォルトでは無効になっています。
  • GitLabセルフマネージドインスタンスで使うには、GitLab管理者に頼んで有効にしてもらいましょう。

JUnit XMLファイルを生成してパイプラインの一部としてアップロードした場合は、パイプラインの詳細ページでレポートを確認できます。このページのTestsタブには、XMLファイルから報告されたテストスイートとケースの一覧が表示されます。

Test Reports Widget

既知のすべてのテストスイートを表示し、それぞれをクリックすると、そのスイートを構成するケースを含む詳細を見ることができます。ケースはステータス順に表示され、失敗したケースが一番上に表示され、次にスキップされたケース、成功したケースが最後に表示されます。

また、GitLab API経由でレポートを取得できます。

JUnit テストレポート機能の有効化

この機能は、:junit_pipeline_view 機能フラグがデフォルトで無効になっています。この機能は、非常に大規模なデータセットでのパフォーマンスに問題があるため無効になっています。パフォーマンスが改善された場合、この機能はデフォルトで有効になります。

この機能を有効にするには、Railsのコンソールへのアクセス権を持つGitLab管理者に以下のコマンドを実行してもらいます。

Feature.enable(:junit_pipeline_view)

# Enable the feature for a specific project
Feature.enable(:junit_pipeline_view, Project.find(<your-project-id-here>))

GitLabでJUnitのスクリーンショットを表示する

  • GitLab 13.0から導入されました
  • フィーチャーフラグで有効・無効を切り替えることができ、デフォルトでは無効になっています。
  • GitLabセルフマネージドインスタンスで使うには、GitLab管理者に頼んで有効にしてもらいましょう。

JUnit XML ファイルにattachment タグが含まれている場合、GitLab は添付ファイルを解析します。

スクリーンショットをアーティファクトとしてGitLabにアップロードします。attachment タグには、アップロードしたスクリーンショットの絶対パスを記述してください

<testcase time="1.00" name="Test">
  <system-out>[[ATTACHMENT|/absolute/path/to/some/file]]</system-out>
</testcase>

この課題が完了すると、添付ファイルがパイプラインの詳細ページに表示されます。

JUnitのスクリーンショット機能の有効化

この機能は、デフォルトで:junit_pipeline_screenshots_view 機能フラグが無効になっています。

この機能を有効にするには、Railsのコンソールへのアクセス権を持つGitLab管理者に以下のコマンドを実行してもらいます。

Feature.enable(:junit_pipeline_screenshots_view)