DMM.comラボエンジニアブログ

DMM.comラボのエンジニアブログです。DMM.comを支える技術について書いています。

DMM insideに引っ越しました。 移転先はこちら -> https://inside.dmm.com/

mocha-phantomjsでクライアントサイドJSのテスト自動化

こんにちはDMM.comラボCTO室の加嵜です。

記念すべきDMMエンジニアブログの第1回目ということで、今回は、mochaというJavaScriptのテスティングフレームワークと、mocha-phantomjsを使って、クライアントサイドのJavaScriptテストの自動化を紹介します。

 

mochaでテスト駆動開発


まず、mochaを使ってクライアントサイトJavaScriptの簡単なテスト駆動開発を実践してみます。

今回使用するのは、mocha本体と、mochaでアサーションを記述するためのchaiというライブラリです。

mocha
http://mochajs.org/
chai
http://chaijs.com/

 

事前準備

事前準備として、htmlで読み込むmocha.js, mocha.css, chai.jsファイルを入手します。npm, bower等を使って自動でダウンロードすることもできますが、今回は最低限のファイルとして、下記の3つを手動でダウンロードしてみます。

mocha.js
https://raw.githubusercontent.com/mochajs/mocha/master/mocha.js

mocha.css
https://github.com/mochajs/mocha/blob/master/mocha.css

chai.js
http://chaijs.com/chai.js

 

上記のファイルを、下のhtmlファイルのように読み込んで、mochaのテストをセットアップします。

mochaのインターフェイスはTDD形式/BDD形式から選べます。今回はBDD形式を採用しています。

chaiのアサーションの形式は、should形式/expect形式/assert形式から選べます。
今回はexpect形式を採用しています。

ファイルの読み込みとJavaScriptのセットアップだけでなく、body内にid=”mocha”のdiv要素を追加するのを忘れないで下さい。

 

test/sample0.html

<!DOCTYPE html>
<html>
	<head>
    	<title></title>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<link rel="stylesheet" href="css/mocha.css" />
    	<script src="js/mocha.js"></script>
    	<script src="js/chai.js"></script>
	</head>
	<body>
    	<div id="mocha"></div>
    	<script>
        	// setup mocha
        	mocha.setup('bdd');
        	mocha.reporter('html');
        	var expect = chai.expect;
        	// validation test
        	describe("サンプルテスト", function() {
            	it("テストケースを記述");
        	});
        	// run test
        	mocha.run();
    	</script>
	</body>
</html>

 

このhtmlファイルをブラウザで開くと、下のような画面になります。

f:id:dmmlabotech:20150710143948p:plain


Step.1 inputのバリデーションテスト実装


今回のサンプルプログラムとして、メールアドレスの入力フォームのバリデーション機能を扱ってみます。

まず、メールアドレスの入力フォームとバリデーションメッセージの出力欄、送信ボタンを持つform要素を作ります。

    	<form id="myform">
        	<input id="validate-mail" type="text" />
        	<span id="invalid-message"></span><br />
        	<input type="submit" />
    	</form>

 

一旦、送信ボタンを押しても実際に送信が実行されないよう、空のファンクションを設定しておきます。

<script src="js/jquery.min.js"></script>
<script>
    	<script src="js/jquery.min.js"></script>
    	<script>
        	$(function() {
            	$('#myform').bind('submit', function(event) {
                	event.preventDefault();
            	});
        	});
    	</script>

 

続いて、mochaのdescribeの中に、このフォームを送信した際のバリデーションの挙動を記述していきます。

 

完成したhtmlファイルが、次のsample1.htmlです。

 

test/sample1.html

<!DOCTYPE html>
<html>
	<head>
    	<title></title>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<link rel="stylesheet" href="css/mocha.css" />
    	<script src="js/mocha.js"></script>
    	<script src="js/chai.js"></script>
	</head>
	<body>
    	<div id="mocha"></div>
    	<form id="myform">
        	<input id="validate-mail" type="text" />
        	<span id="invalid-message"></span><br />
        	<input type="submit" />
    	</form>
        <script src="js/jquery.min.js"></script>
        <script>
            $(function() {
                $('#myform').bind('submit', function(event) {
                    event.preventDefault();
                });
                // setup mocha
                mocha.setup('bdd');
                mocha.reporter('html');
                var expect = chai.expect;
                // validation test
                describe("validation", function() {
                    var $mail = $('#validate-mail'),
                            $form = $('#myform'),
                            $msg = $('#invalid-message');
                    it("empty mail", function() {
                        $mail.val('');
                        $form.submit();
                        expect($msg.text()).to.equal('入力必須です');
                    });
                    it("invalid mail", function() {
                        $mail.val('xxxx');
                        $form.submit();
                        expect($msg.text()).to.equal('不正なメールアドレスです');
                    });
                    it("valid mail", function() {
                        $mail.val('xxxx@xx.com');
                        $form.submit();
                        expect($msg.text()).to.equal('');
                    });
                });
                // run test
                mocha.run();
            });
        </script>
    </body>
</html>

 

このhtmlファイルをブラウザで開くと、まだバリデーション機能を実装していないので、テストが失敗します。

 

f:id:dmmlabotech:20150710143949p:plain

 




Step.2 バリデーション機能実装

テストが失敗することを確認したら、そのテスト成功するように、実際の機能を実装していきます。submit部分のファンクションを下記のように変更します。

 

test/sample2.html

                $('#myform').bind('submit', function(event) {
                    event.preventDefault();
                    var mail = $('#validate-mail').val(),
                        re = /\S+@\S+\.\S+/;
                    if (mail.length === 0) {
                        $('#invalid-message').text('入力必須です');
                    } else if(!re.test(mail)) {
                        $('#invalid-message').text('不正なメールアドレスです');
                    } else {
                        $('#invalid-message').text('');
                    }
                });

 

ブラウザで確認すると、テストが成功に変わりました。続いて、テストが成功することを確認しながら、必要に応じてファンクションの中身をリファクタリングしていきます。

f:id:dmmlabotech:20150710143950p:plain


Step.3 grunt-mocha-phatomjsでテストの自動化

ブラウザでのテストを実行することができましたが、対象ファイルが増えてくると、コマンドですべてのテストを自動化できるようにしたくなります。

最後に、gruntとmocha_phantomjsを導入して、クライアントJavaScriptのテストを自動化してみます。

※ gruntを導入するためにnpmを使用しますので、必要に応じてnpmをインストールしてください。

 

gruntのコマンドを使えるようにします。

npm install -g grunt-cli

 

grunt-mocha-phantomjsをインストールします。

npm install grunt-mocha-phantomjs --save

 

サーバを立ててファイルをhttpでアクセスするために、grunt-contrib-connectをインストールします。

npm install grunt-contrib-connect --save

 

htmlファイルで、mocha.run()となっていた部分を、phantomjsで扱えるように修正します。

 

test/sample3.html

                // run test
                if (window.mochaPhantomJS) {
                    mochaPhantomJS.run();
                }
                else {
                    mocha.run();
                }


Gruntfile.jsファイルを下記のように設定します。

Gruntfile.js

module.exports = function(grunt) {
	grunt.initConfig({
    	connect: {
        	server: {
            	options: {
                	port: 8080,
                	base: '.',
                	keepalive: true
            	}
        	}
    	},
    	mocha_phantomjs: {
        	all: {
            	options: {
                	urls: [
                    	'http://localhost:<%=connect.server.options.port%>/test/sample3.html'
                	]
            	}
        	}
    	}
	});
	grunt.loadNpmTasks('grunt-contrib-connect');
	grunt.loadNpmTasks('grunt-mocha-phantomjs');
	grunt.registerTask('default', ['connect', 'mocha_phantomjs']);
};


gruntのdefaultタスクにconnectとmocha_phantomjsを登録しているので、コマンドラインでgruntを実行すると、サーバが起動し、mochaテストが実行されます。

 

grunt

Running "mocha_phantomjs:all" (mocha_phantomjs) task


  validation
	✓ empty mail
	✓ invalid mail
	✓ valid mail


  3 passing (35ms)

 

 

最後に

クライアントサイドのJavaScriptのテストは、サーバサイドのテストと比べて環境整備が遅れていましたが、mocha-phantomsjsなどの自動化ツールによって実現しやすくなってきました。

さらに、Gruntなどのビルドツールと組み合わせることで、サーバサイドJavaScriptとの連携やminifyなどの作業と統合的に実行することが可能となっています。