LoginSignup
10
8

More than 5 years have passed since last update.

Jestを使わないでReactをUnitTestする(ProxyRequire編)

Posted at

始めに

この記事は以下の記事の続きです。

Jestを使わないでReactをUnitTestする

目的・ゴールはこちらの記事でご確認ください。

試してみる

https://github.com/uryyyyyyy/React_mocha_boilerplate
をダウンロードして、

# node -v => 0.12
npm install -g gulp
npm install

をしたあとに、

gulp test:testUtil --path src/reactComponents/_tests_/nestComponentTest.js

あたりを試してみてください。

(Node.js v0.10.Xで動かすときは、jsdomの設定によってはエラーになります。適宜jsdomをいじってください。)

仕組みの概要

  • mochaベースにする。
  • テスト対象がrequireしているものをproxyrequireでmockする。
  • テストの前にjsx->jsの変換を済ませてしまう。(babelを使う。)
  • 既存のソースコード(jsx, babel書式のコード)に一切手を加えない。

ソースコードは以下に置いています。
https://github.com/uryyyyyyy/React_mocha_boilerplate

gulpfileにwebpackでのbuildも記述しているので、ちゃんと普通のプロジェクトとして動くことも確認できます。

具体的な仕組み

事前にbabel→jsの変換を済ませる。

gulpfile.js
/* prev codes*/

gulp.task('test:compile', ['test:clean'], function () {
    var babel = require("gulp-babel");
    return gulp.src("src/**/*.js")
    .pipe(babel())
    .pipe(gulp.dest("./testSandbox/src"));
});

gulp.task('test:testUtil', ['test:compile'], function () {
    var argv = require('minimist')(process.argv.slice(2));
    var mocha = require('gulp-mocha');
    console.dir(argv);
    return gulp.src('./testSandbox/' + argv.path)
    .pipe(mocha({}));
});

test:compileタスクで、src以下の全ファイルにbabel変換をかけています。
これによって、ソースコード・テストコードをbabelで書いても普通のjsファイルとしてテストができます
(ソースマップは考えてないので、エラーの行番号などはズレますが。。)

「毎回全ファイルを変換するのは重たいのでは?」という懸念がありますが、
今のところ気にならないと思っています。(サンプルプロジェクトの規模なら1秒以内くらい。)
規模が大きくなってきたら、gulpのタスクを分割して対応する方法もあります。

proxyrequire

util2.js
'use strict';
var AsyncUtil = require('./AsyncUtil.js');

export default {
    returnComplicatedResult() {
        return AsyncUtil.dummyPromise().then(v => v + "util2");
    },
    hello() {
        AsyncUtil.hello();
    }
}

mockTest.js
var assert = require('assert');
var proxyquire =  require('proxyquire');

describe("#mockTest", function () {
    var mock = {
        hello() {console.log("hello mock");}
    };
    var util2 = proxyquire("../util2.js",
        { './AsyncUtil.js': mock });

    it("mock hello", function () {
        util2.hello();
    });
});

proxyrequire経由でutil2.jsを読み込ませると、util2.jsがrequireしているAsyncUtil.jsがMockされます。その結果、util2.hello()メソッドはmockオブジェクトのhello() {console.log("hello mock");}を呼ぶことになります。

reactコンポーネントのテスト

nestComponentTest.js
var assert = require('assert');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var proxyquire =  require('proxyquire');

var jsdom = require("jsdom");
global.document = jsdom.jsdom("<!doctype html><html><body></body></html>");
global.window = document.defaultView;
global.navigator = window.navigator;

describe("#react nestComponent test", function () {
    var mock = require("react").createClass({render() {return null;}});
    var NestComponent = proxyquire("../NestComponent.js", {'./UntouchableOne.js': mock});

    it("render NestComponent, but don't render untouchableOne", function () {

        var nestComponent = TestUtils.renderIntoDocument(
            <NestComponent  />
        );
        assert.equal(nestComponent.calc(), 3);

    });
});

jestを使わなくてもそれなりにシンプルに書けますね。

ポイントとしては、

・jsdomを用意してあげる必要がある。
reactのコンポーネントはDOM上でインスタンス生成しなきゃいけないのでjsdomが必要です。

・jsxで書くなら事前に変換を行う。
テストもjsxで書こうと思うと、当然テスト実行前に変換が必要です。(今回はgulpタスクに組み込んでいます。)

・mockを挟むことで、子コンポーネント以下がレンダリングされるのを防ぐ。
普通にコンポーネントをrequireして配置すると、そのコンポーネントの子コンポーネント全てが再帰的にレンダリングされてしまいます。すると、意図しないところで副作用が生じたり時間がかかったりして、テストがやりにくくなります。
今回はproxyrequireを使うことで、子コンポーネント(UntouchableOne.js)をmockに差し替えて、レンダリングされないようにしています。

参考資料

proxyquireでNode.jsのrequireをモックする - ジンジャー研究室

10
8
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
10
8