Nightwatch.jsをE2Eテストフレームワークとして実プロジェクトに適用する時のtipsまとめ
小飼です。
現在開発準備中のプロジェクトのE2EテストフレームワークとしてNightwatch.jsを採用しました。
そこで本稿では、実際のプロジェクトにNightwatch.js
を導入してみるにあたって必要になった知識をまとめます。
Nightwatchとは?
Nightwatch.js is an easy to use Node.js based End-to-End (E2E) testing solution for browser based apps and websites. It uses the powerful Selenium WebDriver API to perform commands and assertions on DOM elements.
Nightwach.js
はNode.jsベースのE2E(End-to-End)テストフレームワークです。
テストブラウザを操作する仕組みであるSelenium WebDriver
のAPIを仲介して各種ブラウザ操作を行い、ブラウザの挙動が期待したものと合致しているかをテストするために用います。
なぜNightwatchなのか?
E2Eテストフレームワークを選定するにあたって、以下の要求を満たすものを考慮しました。
- 今後参画するプロジェクトでも、同じように使っていけること
- 多機能であることよりも、シンプルな機能と安定したAPIの提供を重視していること
- 必要に応じて機能の拡張ができること
- フロントエンドエンジニア以外にも学習コストが高くならないこと
個人的な考えとして、テストフレームワークやテストランナー自体のAPIが複雑化したり、後方互換性のないアップデートをしたためにテスト自体のコストが上がってしまうのでは本末転倒だと思っています。
そこで選定の基準として、なるべく安定したAPIを持ち、今後も安心して使っていけそうなフレームワークであることを重視しました。
さて、本稿執筆時点でE2Eテストフレームワークと呼べる主なものには以下のものがあります。
参考: 『E2E test framework』での検索結果
先に挙げた選考基準を鑑みて、E2Eテストのみを責務としてコストを抑えた導入が出来、今後のプロジェクトで採用するフロントエンドフレームワークの如何に関わらず変わらず活用していけそうなNightwatch.js
を選びました。
インストール
公式ドキュメントのDeveloper Guideに従ってインストールします。
必要なステップは
nightwatch
本体のインストールSelenium Server
のインストール
なお、公式ドキュメントではSelenium Server
をSelenium Download Pageよりダウンロードするよう促されています。
今回はSelenium Server
のダウンロードをnpm
経由で行える、webdriver-managerを使用しました。
Protactor's webdriver-manager as a standalone Node.js module.
このモジュールは、本来Protractor
がSelenium Server
をインストールするために用いていたものを、Protractor
以外のフレームワークでも活用出来るようにforkしたものです。
上述のコマンドは以下のようになります。
# `nightwatch`本体のインストール
npm install --save-dev nightwatch
# `Selenium Server` のインストール
npm install --save-dev webdriver-manager
./node_modules/.bin/webdriver-manager update
# インストールしたいブラウザをオプションとして指定出来る
./node_modules/.bin/webdriver-manager update --chrome
設定ファイルの作成
Nightwatch.js
はプロジェクトのルートに置いてあるnightwatch.json
を参照して起動されます。
公式ドキュメントにある詳細な解説があるので、参考にしました。
{
// テスト環境全体の設定
"src_folders" : [ "test" ],
"output_folder" : "test/reports/",
"custom_commands_path" : "",
"custom_assertions_path" : "",
"page_objects_path" : "",
"globals_path" : "node_modules/babel/register", // テストコードをES2015で書きたいのでBabel.jsを読み込むよ
"selenium" : {
"start_process" : true,
"server_path" : "node_modules/webdriver-manager/selenium/selenium-server-standalone-2.46.0.jar", // webdriver-managerに格納されているSelenium Serverへのパスを記述
"log_path" : "test/logs",
"host" : "127.0.0.1",
"port" : 4444,
"cli_args" : {
"webdriver.chrome.driver" : "node_modules/webdriver-manager/selenium/chromedriver", // webdriver-managerに格納されているchromedriverへのパスを記述
"webdriver.ie.driver" : ""
}
},
// テストするブラウザ毎の設定
"test_settings" : {
// 全てのブラウザに共通の設定
"default" : {
"launch_url" : "http://localhost",
"selenium_port" : 4444,
"selenium_host" : "localhost",
"silent": true,
// テストが失敗した時の検証用にスクリーンショットを撮影しておく
"screenshots" : {
"enabled" : true,
"on_failure" : true,
"on_error" : false,
"path" : "test/screenshots"
},
"desiredCapabilities": {
"browserName": "firefox",
"javascriptEnabled": true,
"acceptSslCerts": true
},
// 各テストファイルから共通して呼び出したい変数を記述
// `this.client.globals`から呼び出せる
"globals": {
"baseUrl": "http://localhost:3000",
"waitTime": 10000
}
},
// ブラウザ毎の設定
"chrome" : {
"desiredCapabilities": {
"browserName": "chrome",
"javascriptEnabled": true,
"acceptSslCerts": true
}
}
}
}
テストコード
各テストはメソッドの形で記述します。
テストケース毎に渡される引数browser
にメソッドチェーンの形でテストシナリオを記述していきます。
export default {
DemoTestGoogle(browser){
browser
.url('http://www.google.co.jp')
.waitForElementVisible('body', waitTime)
.setValue('input[type=text]', 'nightwatch')
.waitForElementVisible(clickBtn, waitTime)
.click(clickBtn)
.pause(waitTime)
.assert.containsText('#main', 'Night Watch')
.end();
}
};
テストファイル・テストケース毎の事前処理・事後処理もメソッドとして記述します
export default {
before(browser) {
},
after(browser) {
},
beforeEach(browser) {
},
afterEach(browser) {
},
DemoTestGoogle(browser) {}
};
非同期なhttpリクエストを含むテスト
ブラウザからの非同期なhttpリクエストを含む機能のテストにおいて、実際に稼働しているサーバーへリクエストを投げて実行してしまうと
サーバーの状態によっては必ずしも同一のレスポンスが帰ってくるとは限りません。
また、フロントエンド側と並行して開発が進んでいる場合、サーバー側のレスポンスが実装されていないケースもあります。
Angular.js
では$httpBackend
サービスを用いて擬似的なレスポンスを返すことが出来ます。
Nightwatch.js
ではこのような仕組みを見つけることが出来ませんでしたので、自前のモックサーバーを用意して代替します。
nightwatch.json
にプロキシサーバーを経由した接続を設定する項目があるので、そちらを使用した方がスマートかも知れません。
事前にリクエスト先のURLが記述されたファイルを用意しておき、bundle
化する時に環境変数を参照してURLを切り替えます。
// ブラウザのリクエスト
imort request from 'superagent';
imort config from './config.js';
const requestURL = config.requestURL;
request.get(requestURL).end((err, ret)=> {
console.log(ret.body);
});
// config.js
let config = {};
if(process.env.NODE_ENV === 'development'){
config.requestURL = 'http://localhost:3000';
}else{
config.requestURL = 'http://api.myserver.io';
}
export default config;
次にテスト環境用のモックサーバーを用意します。
今回は筆者が馴染みのあるNode.js
製サーバーにしましたが、立てやすいものなら何でも良いと思います。
import restify from 'restify';
const AppServerConfig = {
name: 'mock-api-server',
version: '1.0.0'
};
const server = restify.createServer(AppServerConfig);
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(
function crossOrigin(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
return next();
}
);
server.get('/v1/mock', (req, res, next)=> {
res.status(200);
res.send('GET: mock server worked.');
return next();
});
server.listen(process.env.PORT || 3000, function() {
console.log('%s listening at %s', server.name, server.url);
});
ビジュアルリグレッションテスト
プロジェクトの運用フェーズでスタイルシートの変更があった際に、意図しない箇所に変更が起こっていないかを確認する必要があります。
人間の目視に頼った変更確認は不正確且つ高コストになりがちなので、自動化したいところです。
今回はNightwatch.jsの拡張機能として実装している方がいたので、
参考にさせて頂きました。
E2Eテストの実行中にスクリーンショットを撮影して、前回テスト時との差分を検出するカスタムアサーションになっています。
まとめ
以上、Nightwatch.js
でのE2Eテストを導入するにあたって必要なことをまとめてみました。
導入の参考にして頂けたらうれしいです。
今回ご紹介したエンドツーエンドテスト手法を取り入れ、品質の見える化を実現したサービス開発をご検討の企業様は、是非MMMにご相談下さいませ!