TypeScript×Jestで、オブジェクト内のメソッドが関数を呼び出すプログラムのテストがうまくいかない
Q&A
Closed
解決したいこと
typescriptのプログラムを、jestでテストしています。プログラム側ではオブジェクト内に定義されたメソッドが、さらに内部で関数を呼び出します。Jestでは、関数をモック化した上で、テスト内でメソッドを実行します。
テストの実行結果を見るに、①関数がモック化されていない、あるいは ②関数のモック化はできているが、テスト実行時にモック化した関数がよばれていない という状況です。関数を正しくモック化し、テスト実行時にもモック化した関数が動くようにしたいです。
バージョン
- jest:29.7.0
発生している問題・エラー
あるコードがあります。(プロダクトコードと呼びます)
__private__
というオブジェクトが定義され、内部でparent
というメソッドが定義されています。
同じファイルでオブジェクト外にchild
という関数が定義されています。parent
メソッドは、実行時にchild
を呼び出します。
そのコードはこちらです。
//(ファイル名:middleware.ts)
// オブジェクト外で定義される関数。引数を二乗する。
export const child = (num: number) => {
console.log('pdコード');
return num * num;
}
// exportされるオブジェクト。内部にメソッド定義を持つ。
export const __private__ = {
// メソッドparentは、受けた引数をchildに渡す。childの返却値をそのまま返す。
parent(pnum: number) {
const ch = child(pnum);
return ch;
},
};
このmiddleware.tsをテストします。
テストにおいては、__private__
内のparent
を実行し、その中で呼び出されるchild
が返却した値を、常にparent
も返却することをテストします。すなわち、child
の内部実装がなんであれ、parent
がchild
の返却値を脚色・捏造しないことを確かめます。
具体的には以下の方法です。
-
child
をモック化し、常に100を返す様にする。 -
parent
を呼び出し、引数に3を与える(この引数はnumberならなんでもよい) -
child
の返却値をparent
はそのまま返すはずなので、返却値100を期待する。
作成したテストコードは以下です。
//(ファイル名:middleware.test.ts)
import { __private__ } from '../backend/middleware'
// childのみをモック化
jest.mock("../backend/middleware", () => {
const origin = jest.requireActual("../backend/middleware");
// childはテスト内で常に100を返すようにモック化する
return {
__esmodule: true,
...origin,
child: jest.fn().mockReturnValue(100),
}
});
describe('middleware.tsに対するテスト', () => {
test('childが返却した値を、常にparentも返却すること', () => {
const answer = __private__.parent(3);
// childが返した値を脚色せずparentも返す必要がある。それをテストする。
expect(answer).toBe(100);
});
})
test「childが返却した値を、常にparentも返却すること」を実行すると、テストは「不合格」になります。
テスト結果です。
● middleware.tsに対するテスト › childが返却した値を、常にparentも返却すること
expect(received).toBe(expected) // Object.is equality
Expected: 100
Received: 9
20 |
21 | // childが返した値を脚色せずparentも返す必要がある。それをテストする。
> 22 | expect(answer).toBe(100);
| ^
23 | });
24 | })
モック化した返却値の100ではなく、実際に返却されたのは9です。
自分で試したこと
実際のchild側にconsole.log('実際のchild')
を仕込み、テスト実行時に動くchildはプロダクトコードのものか、モック化したものか判別しました。テスト実行時に、「実際のchild」が出力されましたので、間違いなくプロダクトコード側が呼ばれています。
結果から、推測している原因は2つです。(あくまで推測です)
- childのモック化の記述に誤りがあるため、正しくモックになっていない。そのため、実際のchildが呼び出されてしまった
- childのモック化はできているが、別の要因でテストの時は実際のchildが呼び出された
どのような方法なら意図したテストが実施できるか、どうかお力をお貸しいただきたいです。
補足
本件のプロダクトコードは、実際になにかのアプリに組み込むものではありません。ある開発で同ケースの問題があったため、質問用に模倣したものだとお考えください。問題解決のために、プロダクトコード側をリファクタリングするという解決は採用しにくいことをご理解いただけますと幸いです。