LoginSignup
2
2

More than 5 years have passed since last update.

ReactでもKarmaでテスト書いてみる

Last updated at Posted at 2016-08-29

問題提起

なるべくjsdom上でテスト出来ることが望ましいですが、たまにブラウザ上でテストしたくなったり、jquery pluginなどcommonjs化してない(行儀の悪い)ライブラリを使うこともあると思います。その時のテストをkarmaでやってみます。

余談ですが、これをやり始めたのは、Angular2でもテストツールでkarmaを使うっぽいので予習しておこうと思った次第です。

環境

  • NodeJS 5.X~
  • React 15.1
  • TypeScript 1.8

commonjs化されてない(行儀の悪い)ライブラリとして

  • Materialize-css(jquery依存)

を取り入れてみます。

ソースコード全体はこちら

ゴール

  • karmaの挙動を理解する
  • Typescript + commonJSなコードを実ブラウザでUnitTestできる
  • ブラウザで読み込ませる必要のあるコードを含んだテストができる。

karmaの動作の概要

(筆者の理解です。)

  • karmaが起動してブラウザを立ち上げる
  • (polyfillなどの)事前に必要なライブラリなど読み込む。
  • tsやcommonjsのコードはそのままでは読めないのでpreprocesserがjsを生成するのでそれを読む
    • webpackを用いてるので、ts→jsの変換とバンドルまでを行ってくれる。
    • エントリポイントはテストコードになり、それが依存するコードが順にバンドルされる
  • そのjsをブラウザに読ませて、フレームワークがテストを走らせる。
    • 今回はmochaを使っている。
    • アサートライブラリはchaiを使っていて、これはwebpackがバンドルしてくれる。
  • 結果を出力する

動作の詳細とコード

package.json

package.json
  "devDependencies": {
    "chai": "^3.5.0",
    "karma": "^1.2.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-mocha": "^1.1.1",
    "karma-webpack": "^1.8.0",
    "react-addons-test-utils": "^15.1.0",
    "redux-mock-store": "^1.1.1",
    "sinon": "^1.17.4",
    "ts-loader": "^0.8.2",
    "webpack": "^1.13.1"
  },

関連するのはこのあたりです。
karmaの中でwebpack/mocha/chromeを使うので各種インストールしています。

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'],


    // list of files / patterns to load in the browser
    files: [
      'node_modules/jquery/dist/jquery.min.js',
      'node_modules/materialize-css/dist/js/materialize.min.js',
      '**/__test__/*.ts*'
    ],


    // 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__/*.ts*': ['webpack']
    },


    webpack: require(__dirname + '/webpack.config.karma.js'),


    // 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: true,


    // 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,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
};

jsという名の設定ファイルです。

かいつまんで見ていきましょう。(基本コメントについてるのそのままです)

  • basePath
    • 対象とするペースパス。file/excludeとかのベース。
  • frameworks
    • どのテストフレームワークを使うか。
    • 細かいのがいくつかありますが、mocha/jasmineあたりだけ抑えておけば良いのでは
  • files
    • ブラウザに読み込ませるファイル。記述した順番に読まれるっぽい。
    • watch指定できるので、差分があればテストを再開できる。
  • exclude
    • filesで選んだウチの除外対象かな
  • preprocessors
    • 上記でtsコードを指定していますが、そのままでは動かないので前処理をする。
    • 今回はwebpackをかけています。
  • webpack
    • 上記でwebpackと書いたのでその設定ファイル
    • 上記のtestコードをエントリポイントとして、各種tsをjsへバンドルします。
  • browsers
    • テストを動かす対象ブラウザ。別途launcherをインストールしておく必要がある
  • singleRun
    • falseにしたら常駐していて、差分あるたびに再テストされるのかな

webpack.config.js

webpack.config.karma.js
module.exports = {
  resolve: {
    extensions: ['', '.ts', '.js', ".tsx"]
  },
  module: {
    loaders: [
      { test: /\.tsx?$/, loader: "ts-loader" }
    ]
  }
};

karma実行時には、エントリポイントはkarmaが渡してくれるので記述しなくても動作します。
テストコードの方のrequireを辿っていくことで自然とソースコードも含まれるので、それらが個別にバンドルされます。
(テストコードのファイル数分バンドルされるので、テストコードが多いと解決に時間かかりそうです。
たぶん、テスト対象を細かく指定するくらいしか対処法がない。。)

テストコード

基本的には上記で設定完了です。意外とシンプルでした。
一応、テストコードも見てみます。(普通のmochaのコードです。)

Reducer-test.ts
import {assert} from "chai";
import {counter} from "../Reducer";
import {GlobalState, MyAction, ActionTypes} from "../Models";

describe('reducer test', () => {
    it('INCREMENT', () => {
        const state: GlobalState = {num: 4, loadingCount:0};
        const action: MyAction = { type: ActionTypes.INCREMENT, amount: 3};
        const result = counter(state, action);
        assert.deepEqual(result.num, state.num + 3);
        assert.deepEqual(result.loadingCount, state.loadingCount);
    });

    it('DECREMENT', () => {
        const state: GlobalState = {num: -2, loadingCount:0};
        const action: MyAction = { type: ActionTypes.DECREMENT, amount: 10};
        const result = counter(state, action);
        assert.deepEqual(result.num, state.num - 10);
        assert.deepEqual(result.loadingCount, state.loadingCount);
    });

    it('FETCH_SUCCESS', () => {
        const state: GlobalState = {num: -2, loadingCount:1};
        const action: MyAction = { type: ActionTypes.FETCH_SUCCESS, amount: 10};
        const result = counter(state, action);
        assert.deepEqual(result.num, state.num + 10);
        assert.deepEqual(result.loadingCount, state.loadingCount -1);
    });
});

React + Redux + TypeScriptでテストを書く

以前、こちらの記事で書いたコードがそのまま使えます。(chaiとかも、karmaにframeworkとして組み込ませるやり方もあるっぽいのですが、こちらで普通に動いたのでそのまま使っています。)

注意点

jsdomでは、node上で動作するので、ソース/テストコードにnodejs依存の記述があっても動きましたが、karmaだと実行環境がブラウザなので、それらは動きません。例えば、

  • fsなど設定ファイルを読みこむ系のテスト
  • nockなど、httpサーバをモックするテスト
    • Angularでは、フレームワーク側でテストのサポートをしています($httpのDI)。

これは当たり前といえば当たり前で、回避方法はあるのですが、一応共有まで。

まとめ

karma、実ブラウザでテストしてくれるし速度も問題ないので、テストに使うのは良いかもしれないです。

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