Posted at

Cypress で html テストレポート (mochawesome) を出力する


Cypress で html テストレポート (mochawesome) を出力する

Cypress には Cypress Dashboard というテスト実行と実行結果のレポートを生成する素晴らしいサービスがありますが、これを諸事情により使えないけど html テストレポートが欲しいという場合に、自前で生成する方法を説明します。 Dashboard 完全互換ではありません。


下準備

単純な状態を作るために Cypress だけを依存関係に追加したパッケージを作り、初期化します。

yarn init

yarn add --dev cypress
yarn cypress open

この記事では yarn を使います。 npm を利用される方は適宜読み替えてください。

これにより Cypress の設定ファイルである cypress.json が直下に、テストファイル等が cypress ディレクトリ以下に作成されます。


html レポートを出力する


mochawesome をレポーターとして設定する

Cypress は Mocha ベースで作られているため Mocha に対応したレポーターを利用することができます。

今回は mochawesome という html レポーターを利用します。

まずは mochawesome を依存関係に追加しますが、レポーターに peerDependencies が指定されている場合、それも追加する必要があります。

mochawesome には mocha が peerDependencies として指定されているため、 Mocha も追加します。

なお、本記事執筆時点での最新版の Mocha は 6 系ですが、使用すると Cannot read property 'passes' of undefined というエラーが出てしまうため、 5 系 に落とす必要があります。1

yarn add --dev mochawesome mocha@^5.2.0

次に Cypress に mochawesome をレポーターとして設定します。

cypress.json に追記します。


cypress.json

{

"reporter": "mochawesome"
}

この状態でテストを実行してみます。

yarn cypress run

テストが完了すると直下に mochawesome-report というディレクトリができています。

これが、 mochawesome により作成された html レポートになります。

設定により出力ディレクトリを変更することが可能2ですが、今回は出力先を変更しません。

中身を見ると mochawesome.htmlmochawesome.jsonassets というディレクトリができています。3

mochawesome.html というのが出力された html レポートなのでこれを確認してみると、いくつかあるスペックファイルのうちの一つのスペックの結果しか出力されていません。

Cypress はスペック毎に出力を行い、出力のたびにレポートを 上書き するため4で、上書きしないようにする必要があります。

なお、 Cypress がレポートを出力する際、既存のレポートを消すことはないので、テスト実行前に自前でレポートを必ず消す必要があります。


スペック毎にレポートを出力する

スペック毎にレポートを出力するには cypress.json で設定をします。


cypress.json

{

"reporter": "mochawesome",
"reporterOptions": {
"overwrite": false
}
}

この状態で再度テストを実行してみると、 mochawesome-report 以下に連番の html ファイルと json ファイルが作成されます。

中身を確認するとスペック毎に結果が出力されていますが、これだとスペックファイルが増えるたびに参照先が増えることになってしまうので、一つのファイルにまとめるようにします。


mochawesome のレポートをマージする


中間ファイルをマージする

mochawesome のレポートを一つにまとめるには mochawesome-merge というツールを使います。

まずは、依存関係に追加します。

yarn add --dev mochawesome-merge

そして、 cypress.json の設定を変更します。

mochawesome-merge で中間ファイルの json を一つにまとめた後に、別途 html レポートを生成するため、 html の出力を無効にし、 json だけを出力するように変えます。


cypress.json

{

"reporter": "mochawesome",
"reporterOptions": {
"overwrite": false,
"html": false,
"json": true
}
}

この状態でテストを実行すると mochawesome-report 下に json がいくつか作成されます。

これを mochawesome-merge で一つの json にまとめます。

まとめる方法として、 js のスクリプトを書く方法とコマンドラインで実行する方法がありますが、ここではコマンドラインで実行する方法を利用します。5

yarn --silent mochawesome-merge > mochawesome.json

ここでの注意点として --silent オプションを yarn に渡すのを忘れないようにしてください。

渡さないと yarn 自体の出力が json に書き込まれてしまい、 json を読み込もうとした時に文法エラーになってしまいます。

npm scripts の場合には mochawesome-merge > mochawesome.json で問題ありません。

このコマンドを実行すると mochawesome-report 以下の json に出力された結果が、直下の mochawesome.json という一つの json にマージされます。

出力先を変更する場合にはリダイレクト先を変更します。

また、レポート出力先ディレクトリを変更している場合には、 --reportDir という引数にそのディレクトリのパスを渡します。


マージされた中間ファイルから html レポートを出力する

中間ファイルからレポートを出力します。レポートを出力するのに mochawesome-report-generator(marge) を利用します。

marge は mochawesome-report が依存しているため依存関係に追加せずに利用できます。

yarn marge mochawesome.json

引数に html レポートの元となる json ファイルを指定して実行します。

実行すると mochawesome-report 下に mochawesome.htmlassets ディレクトリができ、 mochawesome.html を参照するとすべてのスペックがマージされた結果が出力されていることが確認できます。


出力先の変更

mochawesome-report 下の assets と mochawesome.html をホストすれば結果を公開できるのですが、 mochawesome-report 下には中間ファイルのスペック毎の json があり邪魔です。

邪魔なファイルを消すという手もありますが、 marge には出力先を変更するオプション -o がある6ので、これを利用して mochawesome-report ではないところに出力するようにします。

この後に利用するスクリーンショットや動画の添付の際に相対パスを利用するため cypress 下にレポートを出力するようにします。

mkdir cypress/report

yarn --silent mochawesome-merge > cypress/report/mochawesome.json
yarn marge cypress/report/mochawesome.json -o cypress/report


スクリーンショットや動画をレポートに添付する

Cypress は cypress run での実行時に、テスト失敗時のスクリーンショットおよびテスト結果に関わらずテスト開始から終了までの動画を自動で cypress ディレクトリ以下に保存します。7

しかし、単にレポート出力を行っただけだとレポートには添付されないため、レポートに添付できるようにします。


mochawesome のテスト結果に情報を追加する

mochawesome には各テスト結果に情報を追加する addContext というメソッドがあります。

このメソッドは様々な種類の情報を添付することができる8ので、このメソッドを利用してスクリーンショットおよび動画をテスト結果に添付します。


Cypress で mochawesome の addContext を呼び出す


イベントで addContext を呼び出す

Cypress では Cypress の機能を拡張するプラグインの仕組み9が用意されていますが、今回の目的に適した拡張できるイベントがありません。

after:screenshot というイベントがありますが、スクリーンショットが取得された時だけで動画には対応できませんし、 addContext に渡すテストオブジェクトが取得できません。

そこで今回はデバッグ目的10とされているイベントフックを利用します。

イベントは cypress/support/index.js から以下のようにフックします。11


cypress/support/index.js

Cypress.on(イベント名, コールバック);


利用できるイベントの中にテスト実行後に呼び出される test:after:run というのがあり、

このイベントはテストの各種属性と Mocha の runnable を引数として受け取ります。

このコールバックの中で先ほどの addContext を呼び出します。

スクリーンショットは失敗時のみ取得されるためテスト結果を判定する必要があります。

テスト結果はコールバックの第一引数のオブジェクトの state プロパティが failed かどうかで判定できます。

addContext の第一引数のテストオブジェクトの test というプロパティにコールバックの第一引数をセットしたオブジェクトを渡します。

addContext の第二引数にはスクリーンショットへのパスと動画へのパスを渡します。


cypress/support/index.js

import addContext from 'mochawesome/addContext';

Cypress.on('test:after:run', (test, runnable) => {
if (test.state === 'failed') {
addContext({ test }, 'スクリーンショットへのパス');
}
addContext({ test }, '動画へのパス');
});



スクリーンショットおよび動画のパスの取得

スクリーンショットが保存されるパスはデフォルトでは以下のようになっています。12

{screenshotsFolder}/{specPath}/{testName} (failed).png

各値は以下のように取得できます。


  • screenshotsFolder: スクリーンショットが保存されるベースとなるディレクトリ。設定により変更可能 13 で、デフォルトでは cypress/screenshotsCypress.config('screenshotsFolder') で取得可能だが、絶対パスになるため相対パス化が必要。今回の例ではデフォルト値を利用し起点となるのが cypress/report なので、 ../screenshots になる。

  • specPath: スペックファイルのファイル名。 location.pathname からディレクトリ付きで取得可能だが、 /__cypress/iframes/integration/ディレクトリ/ファイル というような形になるので変換には若干工夫が必要。

  • testName: コールバックの第二引数のプロパティ parent.title および title から取得可能。

動画が保存されるパスは以下のようになっています。

{videosFolder}/{specPath}.mp4

こちらも各値は以下のようになっています。


  • videosFolder: スクリーンショットが保存されるベースとなるディレクトリ。設定により変更可能 13Cypress.config('videosFolder') で取得可能だが、絶対パスになるため相対パス化が必要。

  • specPath: スペックファイルのファイル名。 location.pathname からディレクトリ付きで取得可能だが、 /__cypress/iframes/integration/ディレクトリ/ファイル というような形になるので変換には若干工夫が必要。

上記のようなことから cypress/support/index.js の中は最終的に以下のようになります。


cypress/support/index.js

import addContext from 'mochawesome/addContext';

Cypress.on('test:after:run', (test, runnable) => {
if (test.state === 'failed') {
addContext({ test }, `../screenshots/examples/${location.pathname.replace(/(.*)\//, '')}/${runnable.parent.title} -- ${test.title} (failed).png`);
}
addContext({ test }, `../videos/examples/${location.pathname.replace(/(.*)\//, '')}.mp4`);
});



注意点


  • 上記のパス指定の仕方だとテストのネストの仕方によっては上手く行かないかもしれません。

  • スペックのディレクトリおよびファイル名の取得は手抜きです。

  • 上記はエラー時に自動で取得されるスクリーンショットだけを考慮しており、自前でスクリーンショットを取得する場合14は考慮していません。自前でスクリーンショットを取得したものをレポートに追加するのは、フックできるのが after:screenshot しかなく、前述したように test オブジェクトを取得することができなさそうなので、無理なように思えます。


実行コマンド

最終的な実行コマンドは以下のようになります。

rm -rf mochawesome-report cypress/report

yarn cypress run
mkdir cypress/report
yarn --silent mochawesome-merge > cypress/report/mochawesome.json
yarn marge cypress/report/mochawesome.json -o cypress/report

これであとは以下のディレクトリをホストすればテスト結果をスクリーンショットおよび動画付きで公開することができます。


  • cypress/report

  • cypress/screenshots

  • cypress/videos


サンプル

ここまでをまとめたサンプルは以下のリポジトリにおいてあります。

https://github.com/kobanyan/cypress-mochawesome-example