つらい打鍵テストをCIで回して、テスト結果をWebダッシュボードでチーム内に共有しようというお話です。
はじめまして。枇榔(びろう)です。サーバ内の脆弱性を検出して管理するWebサービス、FutureVuls( https://vuls.biz/ )の開発をしています。
100台から数千台といった大規模なサーバ運用をしている方向けの、サーバ内の脆弱性情報管理を楽にするWebサービスです。OSSの脆弱性スキャナVulsに、チーム運用向けの機能(タスク管理・自動トリアージ・他チームへ情報共有・脆弱性情報のリスト管理など)を追加したものになります。
https://vuls.biz/ の右上から新規登録ですぐに使えるので、ぜひお試しください。
マイクロサービスのテストが打鍵になりがち問題
さて、そんなFutureVuls。画面側はReact、裏側はいくつものコンテナという構成で動いています。
AWS 上でコンテナを立ち上げてマイクロサービスを提供するみたいなこと、増えてきましたよね。マイクロサービスアーキテクチャだとスケール・冗長化が簡単で、障害が置きたときの対応や保守が行いやすいといったメリットもたくさんあります。
しかしながら、導入する上で厄介なのがテスト。各コンテナの中で単体テストがしっかり書かれていても、それぞれのコンテナがきちんと繋がっているかどうか、意図した処理が行われているかどうか確認するため、クライアント端末からのリクエストをもとにしたテストが必要になります。
コンテナの中はmasterにマージする際にテストを回しているけれども、最終的にデプロイを行う際にはExcelのテストケースを見ながら、Web画面をポチポチ叩いての打鍵テストになってしまっている。そんなプロジェクトも多いのではないでしょうか。
Excelのテストケースを見ながらポチポチ叩いて、都度スクリーンショットを撮ってまとめる。そんなテストを何度か行いましたが、それだと気軽にテストとはいかず、リリースを行う際にもテスト工数がかなり必要になりますし、使用しているパッケージのアップデートも行いづらいのでセキュリティ的にも問題が出てきます。
打鍵テストを自動化するOSS Cypress
打鍵テスト自動化したいですよね
できればコーディングに不慣れな人でもテストコードが書けて、動作中の様子をスクリーンショットなどで保存してくれるとエビデンスで使えるので嬉しい。そんな要望に応えてくれるOSSがCypressです

Cypressはブラウザテストツールで、フロントエンドのテストをかなりシンプルに行える仕組みを提供してくれています。フロントエンドの部分だけを単体テストするためにも使えるのですが、統合テストやEnd to End テストにも使える便利な子です。
似たようなツールにSeleniumがあります。両方とも画面のテストに用いられていて、GitHub Actionsにも組み込める同じような使い方ができるツールなのですが少し差異があります。
Seleniumはネットワーク越しにブラウザを自動操作するツールで、スクレイピングとか操作の自動化といったマクロのような使い方をしてテストを行います。
対してCypressはWeb画面のテスト目的に特化したツールです。動的な画面の書き換え完了するまで待ったり、ボタンがdisable設定で表示されているか確認したり、非同期なリクエストが終わるまで待ったりといったことまで行えます。さらに処置は手元で行われるので動作もきびきびとしており、トライ&エラーも行いやすく実行時間も短縮できます。
また、スクリーンショットが欲しいタイミングで cy.screenshot
と入れておくとspecごとに分かれたフォルダにスクリーンショットを保存してくれます。さらにはテスト開始から完了まで実行の様子をmp4形式の動画にしてくれたりもします。
個人的な話ですがたまに疲れたとき、池の鯉を見るような感覚でCypressのテスト実行の様子を眺めていたりします。割と癒やされます。
今回説明すること

導入方法
nodejsの入っている端末でE2E用のリポジトリを作って、
npx cypress open |
で、終わり。簡単。
しばらくダウンロードなどの処理が走ったあと、Cypressのダイアログが表示されます。
試しに actions.spec.js
をクリックすると、テストコードのサンプルとして https://example.cypress.io/commands/actions
へのテストが実行されます。
実行したディレクトリの中には cypress.json
という設定ファイルと cypress
というフォルダができてます。cypress.json
はコンフィグファイルです。タイムアウトまでの時間やスクリーンショットの出力先フォルダを変えたいときはここを変更しましょう。
https://docs.cypress.io/guides/references/configuration.html
cypress
フォルダにはテストコードやプラグインが含まれます。cypress\integration\examples\
内のspec.jsファイルがテストケースのサンプルになっているので、参考に書き換えていきましょう。
Cypressの書き方
Cypressのドキュメント( https://docs.cypress.io/ )を見ると非常に様々なコマンドやテスト方法があるのが分かります。
たくさんあって物怖じしてしまいますが、まずは cy.contain()
cy.get()
cy.click()
の3つを覚えれば簡単な Web ページのテストができるようになります。
/// <reference types="Cypress" /> |
以上を実行すると、こうなります。

左側にテストの内容、右側にテスト実行の様子が表示されます。

テストがすべて完了したあとに実行中の様子を確認できます。
行をクリックすると、どの要素が抽出されて、どこをクリックしたのがまでわかるようになっています。

blog-sidebar
クラス内の Culture
をクリックしているのがわかりますね。
cy.contains
で画面に表示される日本語を選んで click までならコーディングに不慣れな新人さんにも書いてもらえますし、CSSセレクタを理解している人ならgetも使ってもらえます。
ベテランな皆さんには cy.spy()
や cy.stub()
といったものも用意されていますのでご安心を。
また cy.viewport('iphone-6+')
でviewportの動的切替えや、cy.screenshot('top-page-01')
でスクリーンショットの保存などもできます。
GitHub Actionsにつなげる
さて、ブラウザ上で実行結果を確認しつつテストを行えるCypressですが、CUIでも実行できます。
npx cypress run |
こちらの場合は、テストの結果だけ返してくれます。
実行中の様子をあとから確認したい場合は、\cypress\videos\examples\test.spec.js.mp4
として保存される動画ファイルを確認ください。
CUIで実行できるとなればCIに組み込みたいですよね。
では、2019年から使えるようになったGitHub Actionsで動かしてみましょう。
作成したリポジトリ内で以下を実行します。
npm init |
package.json package-lock.jsonの2ファイルが作成されたのを確認したら、GitHubにプッシュし、GitHubのリポジトリページのタブにある「Actions」> Node.js の「Set up this workflow」のボタンをクリック
そして、左側に出てくるエディタに以下を入力します。
name: Node CI |
これで、リポジトリにpushされたタイミングでCIが動くようになりました。
実行結果は以下のように表示されます。

いい感じですね
pushされたタイミングではなく定期的に実行したい場合は
on: [push] |
を
on: |
のようなcron式に書き換えると定期的にテストが走ります。
Cypress Dashboard につなげる
CypressのテストがGitHub Actions上で定期的に実行されて良かったよかった、で終わりたいのですが問題が1つだけ。
実行中の様子をあとから確認できない。
Cypress実行中の様子は \cypress\videos\*.spec.js.mp4
に動画ファイルが保存されているはずですが、CI環境上に保存してしまうので、テスト完了後には環境ごと消されてしまっていて見れなくなってしまいます。
自分でS3あたりにファイルを投げるスクリプトを書かないといけないのか・・・と思っていましたが、Cypressがそのあたりも簡単にする解決策を用意してくれていました。すごい。
Cypress Dashboard というサービスで、Cypress実行中の様子や結果などを見やすくまとめてくれるWebサービスです。
https://docs.cypress.io/guides/dashboard/introduction.html

それではセットアップ方法を見ていきましょう。
npx cypress open |
として、Cypressのダイアログを表示させましょう。

ユーザ登録が終えたら、「Set up project to recoed」の青いボタンを押して設定を完了させます。

すると画面が切り替わり、Project ID と Record key が生成されます。cypress.json
には自動で projectId
が入力されてると思いますので、Record keyと一緒に cypress run
してみましょう。
npx cypress run --record --key {Record key} |
すると「Runs」の画面が変化します。

この #1 の行をクリックするとダッシュボードのページに移動して実行結果が確認できます。

1spec1行で並べられて、各行にOutput・Screenshots・Videoのボタンが用意されています。
OutputにはCUIのログ、Screenshotsにはエラー時のスクリーンショットと cy.screenshot()
の実行結果、Videoにはテスト実行中の様子が動画で保存されます。
Web上に結果が保存されるのでチーム間で共有しやすくもなります。
Dashboardに実行結果が保存されるのを確認したら、GitHub Actionsにも同じようにRecord keyを設定していきましょう。
# test-cy/.github/workflows/nodejs.yml |
書き換えてGitHubにpushすると、GitHubActionsからCypressDashboardに実行結果が送信されます。

まとめ
打鍵テストをCI化するのが、それほど苦労なくできることが伝われば幸いです。
もちろんテストコードを書く必要はありますが、Excelのテストケースに書かれた 申し込みボタンをクリックする
を cy.contains('申し込み').click()
に変えるだけで何100回と使えるコードになるのでコストはペイするはずです。
今回は導入の部分だけを書きましたが、FutureVulsのテストを行う際には
- グループ内の脆弱性情報・タスク情報をDBからクリアする
- スキャン結果をs3にアップロードしてタスクが表示されるか確認する
- 脆弱性のスコアが更新された場合に脆弱性情報が書き換わり、タスクのコメントにログが反映されるか見る
といったこともしています。
これらもCypress(といくつかのプラグイン)で実現できます。すごい。
さくっとテストできる環境を作って、パッケージのアップデートを気楽に行える環境を作っていきましょう。
それでは、よいテストライフを!