これは、モバイルファクトリー Advent Calendar 2018 の11日目の記事です。
こんにちは、id:toricorです。好きな分野はソフトウェアの品質向上・テスト・自動化です。
今日の記事はブラウザ操作自動化ツールを使い、E2Eテストを小さく試したときのお話です。
要約
ブラウザ操作自動化ツールを使いE2Eテストを実行する方法を紹介します(Chromeのみでの実行になります)。
E2Eテストとは
関数などの部品単位を対象にした単体テストに対して、例えばUIを実際に操作してフロントエンド/サーバサイド/DBのすべてが期待通りに動くことを確かめるようなテストはE2Eテスト(End to Endテスト)と呼ばれます。 部品それぞれが正しく動いても組み合わせたときに全体が正しく動くとは限らないので、組み立てたWebページを対象としたテストで動作を見てあげることが必要です。
例えば今日の記事で紹介するようなブラウザ操作自動化ツールを使用することで、ユーザーがWebサービスを利用するときのような一連のブラウザ操作を再現させ、それをテストとして表現することができます。
headless Chromeとpuppeteer
Chromeといえば有名なブラウザですが、headlessモードを使えばGUIなしで高速に動作させる事ができることからE2Eテストで使われています(headless Chrome)。
headless Chromeは開発者が操作しやすいように、DevTools Protocolを介して操作するpuppeteerというNodeのライブラリが提供されており、このライブラリがブラウザ操作を助けてくれます。 github.com
puppeteerの特徴を上げると
- GoogleのDevToolsチームがメンテしてます(コードが長期間メンテされそうですよね!)
- ブラウザ操作でできる大体のことはpuppeteerを介してもできます
- フォームを埋めたり
- SPAをクロールしたり
- デバイスサイズに合わせたスクリーンショットを取ったり
テストを動かす
puppeteer自体はただのブラウザ自動操作ツールなので、テストとしてチェック項目を確認できるようにテスト用のツールも導入します。
テストフレームワークの選定
js界ではいくつか有名なテストフレームワークがありますが今回はJestを採用しました。 jsにはまだそれほど慣れていないので
- ドキュメントが豊富なこと
- すぐ動かせるサンプルコードがあったこと
を基準に選びました。 今回つくったE2EテストはJestの公式ドキュメントにあったサンプルを利用したので基本ケースをすぐに作ることができました。
テストの設定
Jest, puppeteer, jest-puppeteer(puppeteerを使ったテストの設定をいい感じにしてくれる)をインストールします。
__tests__
以下にテストファイルを配置し、yarn test
でテストを実行するようにします。
"scripts": { "test": "jest __tests__", },
実際にテストを実行する際には、環境変数の設定などを行う処理などとともにシェルスクリプトに記述しておき、シェルスクリプトを実行するようにしました。
テスト例
例えば「駅メモ!」の公式サイトの「駅の思い出」ページをテストします。
「駅の思い出」ページでは検索ボックスが1つあり、
- 駅名を検索することで、その駅のページに遷移します
- 駅のページでは駅の情報を表示することができます
今回のテスト例としては、表示される路線情報・都道府県情報が期待通りなのかを確かめます。
const timeout = 5000 ... it('五反田という駅名で検索できる', async () => { const stationName = '五反田' await page.type('.text-input', stationName) await page.click('.search-btn') await page.waitFor('.activity-best-matched-station--result') let text = await page.evaluate(() => document.body.textContent) // 五反田に乗り入れる各路線が期待通り表示されるかどうか expect(text).toContain('JR山手線') expect(text).toContain('東急池上線') expect(text).toContain('都営浅草線') // 五反田は東京都の駅である expect(text).toContain('東京都') // 五反田駅のページのスクリーンショットを取得する await page.screenshot({path: '__tests__/database-gotanda.png'}) }) }, timeout )
テスト結果
前処理-テスト-後処理の順で実行し、テストを通過させることができました。
またスクリーンショットも以下のように簡単に取得することができました。
デバッグ方法
もし意図しない結果を得た場合、下の例のようにheadlessモードをオフにしてテストを実行することでブラウザのウィンドウが立ち上がり、puppeteerが自動で操作する様子を目で見ながら確認することができます。
const browser = await puppeteer.launch({ headless: false, slowMo: 500, // スローモーション })
今後の展望
E2Eテストの対象ページを増やしたり、スクリーンショットの差分チェックテストをしたりできるといいですね!
明日は id:narita-cppです。お楽しみに!