🍣
rewireを使ってexportされていない関数をテストする
こんなケースありませんか?
funcB
は export しているけど、funcA
は export していない。
でも、funcB
の中で、funcA
は呼んでいるので、テストでもちゃんと funcA
が呼ばれているか確認しておきたい。
func.ts
const funcA = () => {
return true;
};
export const funcB = () => {
if (funcA()) { // <- funcAが呼ばれているかどうかをテストしたい
return 'funcA return true';
}
return 'funcA return false';
};
どうやってテストすれば良いのか色々調べていると、rewire
というライブラリを見つけました。
README を見ると以下のように書いてあります。
rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing. You may
- inject mocks for other modules or globals like process
- inspect private variables
- override variables within the module.
セッターとゲッターをテストしたいモジュールに対して付けることで、テストの振る舞いを変えることが出来ると。
後は、プライベートの変数の検出も出来るそう。
しかし、CommonJS
にしか対応していないとの制限がありました。
The current version of rewire is only compatible with CommonJS modules.
使ってみる
$ yarn add -D rewire @types/rewire
テストする関数
func.ts
const funcA = () => {
return true;
};
export const funcB = () => {
if (funcA()) { // <- funcAが呼ばれているかどうかをテストしたい
return 'funcA return true';
}
return 'funcA return false';
};
テスト関数
func.spec.ts
const rewire = require('rewire');
const service = rewire('../../src/libs/func.ts');
const mockFuncA = jest.fn(() => false); // falseにしてみる
service.__set__('funcA', mockFuncA);
describe('func', () => {
test('funcAが1回呼ばれている', () => {
service.funcB();
expect(mockFuncA).toHaveBeenCalledTimes(1);
expect(service.funcB()).toBe('funcA return true');
});
});
export {};
これで実行してみると、以下エラーでそもそもテストが実行出来ませんでした。
...src/libs/func.ts:5
export const funcB = () => {
^^^^^^
SyntaxError: Unexpected token 'export'
1 | export {};
2 | const rewire = require('rewire');
> 3 | const service = rewire('../../src/libs/func.ts');
| ^
4 |
5 | const mockFuncA = jest.fn(() => false);
6 | service.__set__('funcA', mockFuncA);
rewire
が CommonJS
にしか対応していないためです。
これを実行できるようにするためには、実行の仕方を変える必要がありました。
このissueのコメントでも言及されています。
package.json
{
"scripts": {
- "test": "yarn jest",
+ "test": "ts-node -O '{\"module\":\"commonjs\"}' node_modules/jest/bin/jest.js",
}
}
$ yarn add -D ts-node
改めて実行します。
今度はしっかりテストをしてくれました。
実行されているか分かり易いようにあえて落としていますが、
false
を返すようにモックしたので、ちゃんと効いているようです。
expect(received).toBe(expected) // Object.is equality
Expected: "funcA return true"
Received: "funcA return false"
10 | service.funcB();
11 | expect(mockFuncA).toHaveBeenCalledTimes(1);
> 12 | expect(service.funcB()).toBe('funcA return true');
| ^
13 | });
14 | });
15 |
at Object.<anonymous> (test/libs/func.spec.ts:12:29)
Discussion