症状
以下の状況下で、意図したようにモックが作成/適用されない。
- テスト対象が async 関数で複数テストを行う時
- それぞれのテストで異なるモックを使いたい時
例えば以下のような時に、Case1, Case2 でそれぞれモックを定義しているにもかかわらず、Case2 のモックが使用される問題。
class MockTarget { public async func(str: string): Promise<string> { let editedStr: string = str; /* do something */ return editedStr; } } class UsingMockTarget { public async func(str: string): Promise<string> { const mockTarget: MockTarget = new MockTarget(); return await mockTarget.func(str); } }
jest.mock('./MockTarget'); // パスを指定 const Mock: jest.Mock = MockTarget as unknown as jest.Mock; // TypeScriptでは型変換する必要がある test('Case1', async () => { /* Mock 生成 */ Mock.mockReset(); Mock.mockImplementation(() => { return { func: async (str:string) => { if (str == "test") { return "test"; } else { return ""; } } }; }); /* Test実行 */ const usingMockTarget: UsingMockTarget = new UsingMockTarget(); return expect(usingMockTarget.func("test")).resolves.toMatch("test"); }); test('Case2', async () => { /* Mock 生成 */ Mock.mockReset(); Mock.mockImplementation(() => { return { func: async(str:string) => { if (str == "err") { throw new Error(); } else { return ""; } } }; }); /* Test実行 */ const usingMockTarget: UsingMockTarget = new UsingMockTarget(); return expect(usingMockTarget.func("err")).rejects.toThrow(); });
原因
単純な話で、 await によって非同期的にテストが進んでしまうがために、 Case1 のテストが終了する前に Case2 のテストが開始されてモックが上書きされてしまう。
test自体をawait で駆動できれば良いのだが、無理っぽい。少し触った限りでは async 関数のテストをする時には Mock を入れ替えるのは難しそう。
全テストで利用可能な Mock を作らざる終えないが、込み入ったテストをする際に不自由しそう。何か解決策があるのだろうか?
jest.mock('./MockTarget'); // パスを指定 const Mock: jest.Mock = MockTarget as unknown as jest.Mock; // TypeScriptでは型変換する必要がある /* 全てのテストで利用できるような汎用的なモック */ Mock.mockImplementation(() => { return { func: async (str:string) => { if (str == "test") { return "test"; } else if (str == "err") { throw new Error(); } else { return ""; } } }; }); test('Case1', async () => { /* Test実実行 */ const usingMockTarget: UsingMockTarget = new UsingMockTarget(); return expect(usingMockTarget.func("test")).resolves.toMatch("test"); }); test('Case2', async () => { /* Test実実行 */ const usingMockTarget: UsingMockTarget = new UsingMockTarget(); return expect(usingMockTarget.func("err")).rejects.toThrow(); });