コンテンツにスキップ

関数から呼ばれてる関数をspyOnする

最終更新日: 2021-04-01

確認環境

Env Ver
Node.js 12.18.3
Jest 26.4.2

やりたいこと

以下の実装のときに、 parentFunc() を呼んだ時に childFunc() が呼ばれることをテストしたい

1
2
3
4
5
6
7
8
function parentFunc() {
    console.log('called parentFunc');
    childFunc('XXXX');
}

function childFunc(param) {
    console.log(`called childFunc ${param}`);
}

各ケース紹介

Case1 そもそも構文がおかしい

テストするためには関数を export する必要あるが、愚直過ぎて構文的に実行不能になるケース

1
2
3
4
5
6
7
8
9
exports.parentFunc = () => {
    console.log('called parentFunc');
    // childFuncは別モジュールなので呼べない
    childFunc('XXXX');
};

exports.childFunc = (param) => {
    console.log(`called childFunc ${param}`);
};

Case2 スコープ違いでテストが失敗する

この実装を実行すると期待通り動作するので、一見すると大丈夫そうに見える

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function parentFunc() {
    console.log('called parentFunc');
    childFunc('XXXX');
}

function childFunc(param) {
    console.log(`called childFunc ${param}`);
}

module.exports = { parentFunc, childFunc };

しかしこのテストを流すと失敗する これは parentFunc() が呼び出す childFunc() が下記 case2の中にないため parentFunc() のスコープ内に childFunc() がいないことが原因

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const case2 = require('./case2');
// こうするとjest.spyOn()の第一引数を満たせないので落ちる
// const { parentFunc, childFunc } = require('./case2');

describe('inside call test', function () {
    it('parentFunc', function () {
        const spy = jest.spyOn(case2, 'parentFunc');
        case2.parentFunc();
        expect(spy).toHaveBeenCalled();
    });
    it('childFunc', function () {
        const spy = jest.spyOn(case2, 'childFunc');
        case2.parentFunc();
        // childFuncはcase2に属していないため呼ばれない
        expect(spy).toHaveBeenCalled();
    });
});

Case3 テストが成功するケース

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const parentFunc = () => {
    console.log('called parentFunc');
    // parentFunc()の中にchildオブジェクトを注入することで、
    // jestがchildFunc()を認識できるようにする
    child.childFunc('XXXX');
};

const child = {
    childFunc: (param) => {
        console.log(`called childFunc ${param}`);
    },
};

// childFuncでなく、childオブジェクトをexportするのが味噌
module.exports = { parentFunc, child };
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const case3 = require('./case3');

describe('inside call test', function () {
    it('parentFunc', function () {
        const spy = jest.spyOn(case3, 'parentFunc');
        case3.parentFunc();
        expect(spy).toHaveBeenCalled();
    });
    it('childFunc', function () {
        // 注入している側のオブジェクトを参照する
        const spy = jest.spyOn(case3.child, 'childFunc');
        case3.parentFunc();
        // child.childFuncはcase3に属しているため呼ばれる
        expect(spy).toHaveBeenCalled();
    });
});