お久しぶりです。
最近、新しいチームにアサインされ気の向くままに動いているYanagita@宮崎です。
新しいチームでの開発は最初の開発フェーズはほぼほぼ終え、リリースに向け最終テストに入っている段階です。
そこで空いた時間ができ始めたので自動テストの導入検討を開始しました。
今回は、前にaxtstar(@axtstart)氏が書かれた「おためしpuppeteer」に出てきたpuppeteerにテストフレームワークを組み合わせてテストの実行を行いたいと思います。
テストフレームワーク
テストフレームワークでは、出来上がったコードやアプリケーションに対して、テストコードの実行やテスト結果の判定、集計などを行ってくれる枠組みを提供しています。
今回はNode.jsで使用可能なjavascriptベースのテストフレームワークを調べたところ以下が挙げられるのかなと思いました。
- Mocha(https://mochajs.org/) - https://github.com/mochajs/mocha
- Jest(https://jestjs.io/) - https://github.com/facebook/jest
- Jasmine(https://jasmine.github.io/) - https://github.com/jasmine/jasmine
- tape - https://github.com/substack/tape
- QUnit(https://qunitjs.com/) - https://github.com/qunitjs/qunit
- Ava - https://github.com/avajs/ava/blob/master/contributing.md
で、今回はMochaを使用した際の導入からテストコード作成、テスト実行までを紹介したいと思います。
Mochaのセットアップ
npmを使用してMochaのインストールを行います。
※ コマンドはUbuntu上で実行しています。別環境で行う際はそれぞれの環境に読み替えてください。
$ npm install --save-dev mocha /* テストプロジェクトのみにインストール */
puppeteerが未インストールの場合は、puppeteerのインストールも行ってください。
mochaのモジュールは「./node_modules/mocha/bin/mocha」になりますが、毎回binまで潜って実行するのは面倒なのでプロジェクトディレクトリのルートにpackage.jsonを作成しておきます。
{ "scripts": { "test": "mocha" } }
作成が完了したら、「npm test」でテストの実行が可能です。
$ npm test > @ test /home/t.yanagita/workspace/mocha-project > mocha Warning: Could not find any test files matching pattern: test No test files found npm ERR! Test failed. See above for more details.
ERR!となっていますがテストコードが無いので今は問題ありません。
テストコード
テストサンプル用のWebページはこちら、サンプルなので特に入力チェック等は行っていません。
- 入力ページ(index.html)
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Input Page</title> <!-- https://github.com/pure-css/pure/ --> <link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <form action="./complete.html" method="post" class="pure-form pure-form-stacked"> <fieldset> <!-- 見出し --> <legend>Test Form</legend> <!-- メールアドレスフォーム --> <label for="mail_address">Mail Address</label> <input id="mail_address" type="text" name="mail_address" placeholder="mail address"> <!-- パスワード --> <label for="password">Password</label> <input id="password" type="password" name="password" placeholder="Password"> <!-- 送信ボタン --> <button type="submit" class="pure-button pure-button-primary">login</button> </fieldset> </form> </body> </html>
- 完了ページ(complete.html)
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Complete Page</title> <!-- https://github.com/pure-css/pure/ --> <link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" > <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <fieldset> <legend>Complete</legend> <a class="pure-button" href="./index.html">Back</a> </fieldset> </form> </body> </html>
- テストコードはこちら(./test/test.js)
Mochaでは、プロジェクトルートディレクトリの直下にtestディレクトリを作成し、そのtestディレクトリ配下にテストコードを配置します。
const puppeteer = require('puppeteer'); const assert = require('assert'); describe('テストサンプル', function() { // mocha設定 this.timeout(0); /* 解説1 */ const WAIT_FOR_TIMEOUT = 2500; /* ms */ const VIEW_WIDTH = 1024; /* px */ const VIEW_HEIGHT = 768; /* px */ // テスト前処理 before(async function() { global.browser = await puppeteer.launch({ headless: true, ignoreHTTPSErrors: true, /* 解説2 */ arg: [ `--window-size=${VIEW_WIDTH},${VIEW_HEIGHT}` ] }); }); // テストケース it ('テストケース', async function() { const page = await global.browser.newPage(); await page.setViewport({ width: VIEW_WIDTH, height: VIEW_HEIGHT }); /* 解説3 */ await page.goto('http://localhost/index.html'); await page.waitFor(WAIT_FOR_TIMEOUT); /* 解説4 */ assert.equal(await page.title(), 'Input Page'); /* 解説5- 1*/ // スクリーンショット - 入力前 await page.screenshot({path: 'input_form_before.png', fullPage: true}); /* 解説6 */ // テストデータ入力 await page.type('[name=mail_address]', 'test.user@astamuse.co.jp'); await page.type('[name=password]', 'password'); // スクリーンショット - 入力後 await page.screenshot({path: 'input_form_after.png', fullPage: true}); // submit await page.click('[type=submit]'); await page.waitFor(WAIT_FOR_TIMEOUT); /* 解説4 */ // スクリーンショット - 完了画面 await page.screenshot({path: 'complete_page.png', fullPage: true}); assert.equal(await page.title(), 'Complete Page'); /* 解説5 - 2 */ const result = await page.$eval('legend', node => node.innerText); assert.equal(result, 'Complete'); /* 解説5 - 3 */ }); // テスト後処理 after (function () { // ブラウザ閉じます。 global.browser.close(); }); });
mochaとおためしpuppeteerになかったpuppeteerの実装部分について補足します。
解説1 mochaでは1テストケース毎のタイムアウト時間が設けられています。デフォルトでは2000msとなっているので、テスト実施にかかる時間に合わせてタイムアウト時間を増やすか、0を設定することでタイムアウトを無効化できます。(サンプルは無効化してます。)
解説2 puppeteerのテストサーバが自己証明書を使用している場合、「 ignoreHTTPSErrors: true」で自己証明書のエラーを回避します。(デフォルトはfalse)
解説3 headlessブラウザのウィンドウサイズを設定します。widthのサイズはWebページの最小幅より狭く設定すると画面キャプチャ時に切れてしまいます。
解説4 ページ遷移中の待機時間を設定します。時間に待機以外にも、Selectorを設定することで次のページのElementが見つかるまでやfunctionを使用して特殊な待機条件を作成することが可能です。
解説5 ページ内のElementから情報を引き出してassert.equalで検証を行います。タイトル情報はtitleメソッドが用意されていますが他のElement操作にはpage.$、page.$$, page.$eval、page.$$evalメソッドが用意されています。
解説6 ページ全体を画面キャプチャする際は、オプションに「fullPage: true」を追加する。(デフォルトはfalse)
テスト実行
インストール完了時に実行した「npm test」でテスト実行します。
テストコードが正常に終了した場合は
$ npm test > @ test /home/t.yanagita/workspace/mocha-project > mocha テストサンプル ✓ テストケース (7262ms) 1 passing (12s)
passingが正常にテストを終えた件数です。
解説5 - 3の第ニ引数を'failure'に変更して、エラーが発生した場合は
npm test > @ test /home/t.yanagita/workspace/mocha-project > mocha テストサンプル 1) テストケース 0 passing (7s) 1 failing 1) テストサンプル テストケース: AssertionError: 'Complete' == 'failure' + expected - actual -Complete +failure at Context.<anonymous> (test/test.js:56:12) at process._tickCallback (internal/process/next_tick.js:109:7) npm ERR! Test failed. See above for more details.
failingが失敗したテストの件数です。NG部分の結果も合わせて出力されます。
補足1:testディレクトリのサブディレクトリにテストコードを配置した場合は、「npm test test/*」でサブディレクトリを含めたテストコードの実行が可能です。
補足2:特定のテストコードを実行する場合は、「npm test test/test.js」でtest.jsのみの実行が可能です。
さいごに
puppeteer + machaでの導入、テストコード作成、実行するところまでさっくりと済ませました。ポイントさえ押さえれば引っかかること無く実行まで行えると思います。
感じたことは、Mochaに限らず最初に挙げたフレームワークは他の言語のテストフレームワークと似た構成となっているので、これまでにテストフレームワーク(今のチームだとサーバサイドはSpecs2)を使用してテストコードを作成した経験があれば学習コストをあまり掛けずに導入できるかなと感じました。(別チームで使用しているPhantomJSと比較した個人の感想)
お願い
アスタミューゼではたくさんのエンジニア&デザイナを募集しています。
気になる方は下からご応募下さい!新しい出会いをメンバー一同お待ちしてます!
(地方の方も遠慮せずご応募ください!きっと柔軟に対応してくれます!!!)