LoginSignup
6
6

More than 5 years have passed since last update.

node.js + ローカルインストールmochaをWindowsで使う

Last updated at Posted at 2016-09-12

はじめに

node.js にテストフレームワーク導入する方法って話。
普通にやれば困らずに出来る範囲なんだけど
「普通」に未だ辿りついてない私レベルの方々の参考に成れば!、
っていう話。
実行方法と、実行していく中でハマッた事例を記載。

ネットをザッと検索した印象では、現状(2015~2016)だと
「とりあえずMochaでいんじゃね?」っと感じられたので、
ここでは数あるJavaScriptのテストフレームワーク中で
mochaを採用する。

なお、mochaは、jQuery環境(ブラウザ環境)でも使えるみたい。
でも以下ではコマンドライン前提(node.js)。

Mochaの実行ファイルのありかと、テストコードの実行方法

npm install mocha でローカルインストールする。
多くの解説サイトでは、この直後に「mochaでテストを実行可能」って
書いてあるけど、それはLinux環境前提な。

Windows環境の場合は、パスが通っていないのにで、NG

実行ファイルが「node_modules.bin\mocha」辺りにあるので、
「node_modules.bin\ 」をNODE_PATH環境変数に設定するか、もしくは、
以下を記述したバッチファイル mohca.bat を test フォルダの上位に置いておく。

@node_modules\.bin\mocha %1 %2 %3 %4 %5 %6 %7 %8 %9

※配置は以下を前提。

mocha.bat
+node_modules
+src
| +― 被テストコード.js

+test
  +― テストコード.js

これで、コマンドプロンプト上から
mocha
で実行可能になる。
※もちろんテストコード.js」が無ければ何も走らない。

テストコードは、以下のようにして書くが、詳細は割愛。

test.js
describe( "テストグループ。ファイル名とか", function(){
    describe( "メソッド名とか",function(){
        it( "テストの内容",function(done){
        /* assert等を用いてテストコードを書く */
        }
    }
}

mocha test\テストドライバー.js でファイル単位で実施可能。
指定しない場合は、testフォルダの中全部が実行される。

※以降のサンプルコードと環境は、chaisinon の利用を前提。
npm chai sinon でインストールすること。

【注意】Mocha+非同期がエラーする場合の対処方法。

正確には、
「アサーション失敗(NG)ったときに、
 エラー内容の表示ではなくタイムアウトエラー扱いされる」
不具合の対処方法。

「期待値はhogeですが、実行結果はpiyoでした。失敗」
が欲しいのに、
「テスト実行はtimeoutしました」って言われて、
それじゃねーんだよ、欲しいのは!、な例。

これは、
「Promise() ベースの非同期実装している場合は、
 done()を呼び出すのではなく、
 Promise.then()のインスタンスを返却する」
ことで回避できるみたい。

以下、サンプルコード。
先ずはテストされるコード。1秒後にcallback()を呼ぶ。

promise_trial.js
var p = function( argv, callback ){
    var promise = new Promise( function(resolve, reject){
        setTimeout( ()=>{ 
            resolve(); 
        }, 1000 );
    });

    return promise.then(function(){
        console.log( argv );
        callback( argv );
    })

}
exports.p = p;

続いて、テストコード。
最初の2つは、done();で実装。
正常系は問題ないが、異状系が「タイムアウト」って言われる。
これを修正したのが、後の2つ。
異状系でも、ちゃんと「~がNG」と言ってくれる。

promise_trial_test.js
const chai = require("chai");
const assert = chai.assert;
const expect = chai.expect;
const sinon = require("sinon");
const target = require("../src/promise_trial.js");

describe( "Promiseでの非同期実装のテスト方法", function(){
    describe( "正常系はOK:done()利用",function(){
        it( "done()で完了通知",function(done){
            target.p("hoge", function(argv){
                expect( argv, "正常系は問題ない" ).to.equal("hoge");
                done();
            });
        });
    });

    describe( "異常系はタイムアウト扱いされる:Done()利用",function(){
        it( "done()で完了通知",function(done){
            target.p("piyo", function(argv){
                expect( argv, "異常系を拾えない" ).to.equal("hoge");
                done();
            });
        });
    });

    describe( "Rromiseをreturnすることで、エラーも正しく拾える:正常系",function(){
        it( "done()ではなくPromiseで完了通知",function(){
            var stub = { "callback" : ( argv )=>{ /* 処理無し */ } };
            var spyCallback = sinon.spy( stub, "callback" );

            var promise = target.p("hoge", stub.callback);
            return promise.then( function(){
                expect( spyCallback.getCall(0).args[0]).to.equal("hoge");
            });
        });
    });

    describe( "Rromiseをreturnすることで、エラーも正しく拾える:異常系",function(){
        it( "done()ではなくPromiseで完了通知",function(){
            var stub = { "callback" : ( argv )=>{ /* 処理無し */ } };
            var spyCallback = sinon.spy( stub, "callback" );

            var promise = target.p("piyo", stub.callback);
            return promise.then( function(){
                expect( spyCallback.getCall(0).args[0]).to.equal("hoge");
            });
        });
    });

});
// アロー関数が混在しているのはスルーでよろ。

chaiとsinon に関する備忘録。

chai のdeepメソッドを用いてオブジェクトの中身を検証

var func = function(){ return { /* オブジェクトを返す */ }; }

expect( func(), "オブジェクトの中身で検証する" )
.to.depp.equal( { /* 期待値をザッと書く */ } );

スパイ、スタブ、モック、の利用用途との違い。

spy()
 呼び出し先のメソッドが既に存在する(機能する)時に使う。
 呼び出し先のメソッドはそのまま実行。
 何回呼び出されたか? 何の引数で呼び出された?等をチェックする。

stub()
 呼び出し先のメソッドが未だ無い、時に使う。
 呼び出し先のメソッドは実行しない。

mock()
 Stub()の機能にプラスして、
 何回呼び出された? 何の引数で呼び出された?等をチェックする
 stub() + spy() のイメージ。

(ちょっと違うかもしれないが…)

※なお、ここで「呼び出し先のメソッドを実行しない」などの
 Overrite動作が出来るのは、テストコード内のみ。
 非テストコードのAファイル内で参照されている変数は、
 例えexportsされていたとしても
 弄ることはできないっポイ(こちらで検証してみた)。

参考にしたページ

mochaとchaiとsionn関連

[Expect / Should - Chai]
http://chaijs.com/api/bdd/

[npm と Node.js 上で require を使ったモジュール読み込みの仕組みについてメモ | phiary]
http://phiary.me/npm-node-js-require-module-memo/

[javascriptのテストのはなし:Sinon.JS(その1) | Developers.IO]
http://dev.classmethod.jp/etc/javascript_testing_framework_sinonjs-1/

[jsでTDD!MochaとChaiとsinon.js入門 - lxyuma BLOG]
http://lxyuma.hatenablog.com/entry/2013/12/15/211637

[mochaとchaiの最も基本的な使い方 - 30歳からのプログラミング]
http://numb86-tech.hatenablog.com/entry/2016/06/08/155834
⇒「ブラウザでのmocha実行方法」も載ってた。

mocha+Promiseな非同期環境でエラー拾えない。

[Promise な関数を mocha でテストする - ぶるーすくりーん]
http://mid0111.hatenablog.com/entry/2015/08/23/214647
>「expect は hoge なんだけど実際は fuga だったよ」って言ってほしいのにタイムアウトエラーになる。

[Bluebirdを利用したコードをMochaでテストする - Qiita]
http://qiita.com/ysm001/items/c1070a363857a9118c89
>assertを呼び出しエラーが起きた時点でcatch handlerに飛ぼうとするため、後続のdoneが実行されずにtimeoutとなる

[Mocha - the fun, simple, flexible JavaScript test framework]
https://mochajs.org/
>WORKING WITH PROMISES

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6