11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Karma+mocha+power-assertのセットアップ

Last updated at Posted at 2014-08-05

はじめに

以前、JavaScriptのユニットテスト環境を、Testem(テストランナー)+mocha(テスティングフレームワーク)+chai(アサーションライブラリ)という構成にしていました。

この構成でも特に問題はなかったのですが、Karma for JavaScript test runner - blog.koba04.comを拝見して「Karma良さそう…!」と思い、今はKarma+mocha+power-assert(タスクランナーはGrunt)という構成で落ち着いています。
(ちなみに、power-assertはシンプルなアサーションと、テスト失敗時の詳細かつ直感的なログ出力がとても素敵です><)

この記事は、Karma+mocha+power-assertの構成をどのように構築したかの備忘録です。Node.jsやGrunt、Karmaなどのインストール手順は省略しております。

目的

この構成で実現したいユニットテストの方法としては、テスト対象のjsファイル、もしくはテストコードを記述したjsファイルを変更した際に、Chrome上で自動的にテストを実行したいというものです。

なお、アサーションライブラリにpower-assertを利用する場合は、power-assert用にコードを変換し、その変換後のコードに対してテストを実行する必要があります。
(このあたりの仕様はpower-assertのGithubを参照のこと)

フォルダ構成

今回の記事が対象としているフォルダ構成は下記の通りです。

Root
│  Gruntfile.js
│  karma.conf.js
│  package.json
│  index.html
│
├─css
│    index.css
│
├─js
│  │  index.js
│  │
│  └─vendor
│          jquery-2.1.0.js
│
│
└─test
   │  index_test.js
   │
   └─vendor
           power-assert.js

必要なGruntプラグインのインストール

grunt-este-watch

ファイルの変更を検知するプラグイン

npm install grunt-este-watch --save-dev

grunt-karma

KarmaをGruntから実行するためのプラグイン

npm install grunt-karma --save-dev

grunt-espower

espower(power-assert用にコードを変換するツール)のプラグイン

npm install grunt-espower --save-dev

Gruntfile.js

Gruntfile.jsの設定内容は下記の通りです。

ここで定義している処理の流れは

  1. js/index.jsもしくはtest/index_test.jsが変更・保存されたのを検知する
  2. index_test.jsをpower-assert用に変換する
    1. の変換後のソース(test/espowered/*.js)を対象にテストを実行する

です。

Gruntfile.js
module.exports = function (grunt) {
    'use strict';

    var pkg = grunt.file.readJSON('package.json');

    grunt.initConfig({
        pkg: pkg,
        // power-assert用にコードを変換するタスク
        espower: {
            test: {
                files: [
                    {
                        expand: true, 
                        cwd: 'test/',             // 変換対象のファイルを配置しているディレクトリ
                        src: ['*.js'],            // 変換対象のファイル名
                        dest: 'test/espowered/',  // 変換後のコードを配置するディレクトリ
                        ext: '.js'                // 変換後のファイルの拡張子
                    }
                ]
            }
        },
        // ファイルの変更検知用タスク
        esteWatch: {
            options: {
                dirs: ['js', 'test'],
                livereload: false
            },
            js: function (filepath) {
                return ['test'];
            }
        },
        // Karma実行用タスク
        karma: {
            unit: {
                options: {
                    configFile: 'karma.conf.js',
                    autoWatch: true,
                    browsers: ["Chrome"],
                    reporters: ["progress"],
                    singleRun: true,
                    keepalive: true
                }
            }
        }
    });

    // package.json内のdevDependenciesで定義されているパッケージ中『grunt-』で始まるものをロードする
    for (var taskName in pkg.devDependencies) {
        if (taskName.substr(0, 6) == 'grunt-') {
            console.log('load npm task -> ' + taskName);
            grunt.loadNpmTasks(taskName);
        }
    }

    grunt.registerTask("default", "esteWatch");
    grunt.registerTask('test', ['espower', 'karma']);
};

karma.conf.js

karma.conf.jsの設定内容は下記の通りです。

karma.conf.js
module.exports = function (config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',


        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['mocha', 'mocha-debug'],


        // list of files / patterns to load in the browser
        files: [
            'js/vendor/jquery-2.1.0.js',
            'js/*.js',
            'test/vendor/power-assert.js',
            'test/espowered/*.js'
        ],


        // list of files to exclude
        exclude: [

        ],


        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {

        },


        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],


        // web server port
        port: 9876,


        // enable / disable colors in the output (reporters and logs)
        colors: true,


        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,


        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: false,


        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],


        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};

実行方法

プロジェクトのルートディレクトリに移動し、コマンドラインから下記を実行してください。

$ grunt

gruntコマンドを引数なしで実行しているため、Gruntfile.jsで定義しているデフォルトのタスク(『grunt.registerTask("default", "esteWatch");』)が実行されます。

esteWatchはテスト対象のコード(js/index.js)およびテストコード(test/index_test.js)の変更を監視しています。
これらのファイルに変更があれば、前述の処理の流れでテストを実行します。

実行結果

ファイル変更・保存時→テスト失敗

テスト失敗時の例として、こういったサンプルにありがちな、Greetingオブジェクトのgreet関数のテストを実行してみたいと思います。

テスト対象のコード(js/index.js)

js/index.js
window.ns = {};

(function (ns) {
    'use strict';

    var Greeting = ns.Greeting = function (name) {
        this.name = name;
    };

    Greeting.prototype.greet = function () {
        return 'Hello, ' + this.name + '!!';
    };
}(ns));

テストコード(test/index_test.js)

test/index_test.js
describe('Greeting', function () {
    describe('#greet()', function () {
        it('Greetingをnewする時にnameを渡さなかった場合', function () {
            var greeting = new ns.Greeting();
            assert(greeting.greet() === 'Hello, unknown!!');
        });
    });
});

test/index_test.jsか、js/index.jsを変更・保存すると、下記の通りテストが実行されます。

Running "esteWatch" task
>> Waiting...
>> User action.
>> File changed: test/index_test.js

Running "espower:test" (espower) task

Running "karma:unit" (karma) task
INFO [karma]: Karma v0.12.19 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 36.0.1985 (Mac OS X 10.9.2)]: Connected on socket hklYkb1RNy6QzmNzoXL1 with id 78195425
Chrome 36.0.1985 (Mac OS X 10.9.2) Greeting #greet() Greetingをnewする時にnameを渡さなかった場合 FAILED
        # test/index_test.js:5

        assert(greeting.greet() === 'Hello, unknown!!')
               |        |       |                      
               |        |       false                  
               |        "Hello, undefined!!"           
               Object{name:undefined}                  

        --- [string] 'Hello, unknown!!'
        +++ [string] greeting.greet()
        @@ -6,11 +6,13 @@
         , un
        -known
        +defined
         !!

ファイル変更・保存時→テスト成功

テストが通るようにテスト対象のコード(js/index.js)を変更・保存すると、下記の通りテストが実行されます。

Running "esteWatch" task
>> Waiting...
>> User action.
>> File changed: test/index_test.js

Running "espower:test" (espower) task

Running "karma:unit" (karma) task
INFO [karma]: Karma v0.12.19 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 36.0.1985 (Mac OS X 10.9.2)]: Connected on socket b1JTt49h-waXPjMA6Jb2 with id 87727203
Chrome 36.0.1985 (Mac OS X 10.9.2): Executed 3 of 3 SUCCESS (0.129 secs / 0.003 secs)

Running "esteWatch" task
>> Waiting...

これでテストがさくさく書けますね!

11
11
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
11
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?