問題
クラスのインスタンスメソッド内で、APIで何かをfetchしてくるケースを考えます。たとえば以下のようにgetGlobalIp()
でグローバルIPを取得。
import axios from 'axios';
export class Hoge {
getGlobalIp = async (): Promise<string> => {
const { data } = await axios.get<{ origin: string }>('https://httpbin.org/ip');
return data.origin;
};
}
この戻り値が常に固定値になるように、jest.spyOn
でモック化してみます。
import { Hoge } from './Hoge';
it('グローバルIPアドレスを取得', async () => {
jest.spyOn(Hoge.prototype, 'getGlobalIp').mockReturnValue(Promise.resolve('8.8.8.8'));
const hoge = new Hoge();
expect(await hoge.getGlobalIp()).toBe('8.8.8.8');
});
すると、because it is not a function
とか叱られてエラーになっちゃいました。
FAIL test/Hoge.test.ts
✕ グローバルIPアドレスを取得
● グローバルIPアドレスを取得
Cannot spy the getGlobalIp property because it is not a function; undefined given instead
2 |
3 | it('グローバルIPアドレスを取得', async () => {
> 4 | jest.spyOn(Hoge.prototype, 'getGlobalIp').mockReturnValue(Promise.resolve('8.8.8.8'));
| ^
5 |
6 | const hoge = new Hoge();
7 | expect(await hoge.getGlobalIp()).toBe('8.8.8.8');
at ModuleMocker.spyOn (../../node_modules/jest-mock/build/index.js:783:15)
at Object.<anonymous> (test/Hoge.test.ts:4:8)
TypeScriptで書いてるので、エディタでエラーが出ないってことはメソッドはちゃんと存在するということ。
最初ぜんぜん意味がわからずドハマリしたのですが、一応回避策が見つかったので書いておきます。
対策
インスタンスメソッドの書き方が問題でした。
クラスフィールド(インスタンス変数)に関数オブジェクトを代入する記法だと、jest.spyOn
では関数として認識してくれないのです。
アロー関数でもfunctionでも、どちらもNG。
export class Hoge {
getGlobalIp = async () => {};
}
export class Hoge {
getGlobalIp = async function () {};
}
メソッド構文で書くと、ちゃんとspyOn
成功します。
export class Hoge {
async getGlobalIp() {}
}
修正版の全体
import axios from 'axios';
export class Hoge {
async getGlobalIp(): Promise<string> {
const { data } = await axios.get<{ origin: string }>('https://httpbin.org/ip');
return data.origin;
}
}
実行結果
PASS test/Hoge.test.ts
✓ グローバルIPアドレスを取得 (1 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.501 s, estimated 4 s
おわりに
this
の関係でクラスフィールド&アロー関数でメソッド定義したいことも多いから、クラスフィールドで定義したメソッドもspyOn
できるようにならないもんかなぁ・・・
誰か詳しい人いたら教えて下さい!
ちなみに、クラスメソッド(static
)は、クラスフィールドとしてメソッド定義してもちゃんとspyOn
できました。インスタンスメソッドも、Jestのバージョンアップで対応される可能性はありそうですね。
この記事で使ったライブラリのバージョンは以下です。
- typescript@4.7.4
- jest@28.1.3