0
0

More than 1 year has passed since last update.

Node.js (TypeScript) で class + arrow function の jest を使ったテスト

Posted at

はじめに

最近の TypeScript なら、関数は以前から存在する 'function' ではなくアロー関数を使うのが一般的だろう。しらんけど。
昨今はフロントエンドの隆盛もあってか、あるいは関数型言語からの影響か、そもそも js はクラスベースじゃないからなのか、わざわざ class にすることは少ないかもしれないが、私は日頃 c++ でなんでも class ベースで実装して暮らしているので class にまとめてしまう。そしてテストを書こうとする度に mock の使い方を忘れて調べ直すのだった。

もくじ

環境

  "jest": "^26.6.3"
  "jest-cli": "^26.6.3"
  "ts-jest": "^26.4.4"
  "typescript": "^4.0.5"

class の arrow function

  • インスタンス経由で呼び出すおそらくクラスで一番よく出てくる普通の関数
export class SoundPlayer {
    public PlaySoundFile = () => {
        ...
    }
}

結論

  • jest.mock + テストで呼び出される arrow function に mock をセット
jest.mock('SoundPlayer');

test('SoundPlayerConsumer call SoundPlayer.PlaySoundFile', () => {
    mocked(SoundPlayer).prototype.PlaySoundFile = jest.fn(() => {});
    expect(mocked(SoundPlayer).prototype.PlaySoundFile).toBeCalled();
});

説明

jest.mock('path/to/SoundPlayer') では arrow function は置き換わらないが、個別にモック関数を指定すれば
インスタンスメソッドを置き換える事ができる。

ちなみに単に jest.mock した状態で arrow function が呼び出されると関数ではないと怒られる

class Dog {
    public Bark = () => {
        console.log('bow wow');
    }
}
...
jest.mock('path/to/SoundPlayer');
test('it failed', () => {
    const player = new SoundPalyer('sound.mp3');
    player.PlaySoundFile(); // > TypeError: player.PlaySoundFile is not a function
})

クラスの prototype をモックに変えればインスタンスのメソッドが差し替わる事とイコールであることは理解できる。

jest.mockにより元の arrow function は消えてしまい、prototype の属性としてモックを指定していると考えればいいのだと思う

class の static な arrow function

class SoundPlayer {
    public static Play = (file: string) => {
        ...
    }
}

結論

  • jest.mock で OK
jest.mock('SoundPlayer');

test('Mock Static Methode', () => {
    SoundPlayer.Play('Last Christmas');
    expect(mocked(SoundPlayer).Play).toBeCalled();
});

テストの後は jest.resetAllMocks などでモックの状態をリセットすることを忘れないように

class の 非arrow function

  • これは jest.mock で OK. すべての処理をモックにする必要がない場合や、単に spy 的な 機能を利用したい場合は jest.spyOn を使うといいだろう (メソッドについては prototype を利用する)
const m = jest.spyOn(SoundPlayer.prototype, 'PlaySoundFile');

mock と元の振る舞い両方を利用したい場合

    test('mocked Bark', async() => {
        jest.resetModules();

        jest.doMock('./dog'); // Dod クラスを mock 化
        const { Dog } = await import('./dog'); // import

        mocked(Dog).prototype.Bark = jest.fn(() => {
            console.log('called mocked Bark!!');
        }); // Bark の振る舞いをモックに差し替える

        const { Breeder } = await import('./breeder'); // Breeder は Dog クラスを利用する
        // Dog を mock 化した上で import することで、 Breeder は mock 化された Dog クラスを利用する
        const b = new Breeder();
        b.Show();
    }
    test('original Bark', async() => {
        jest.resetModules();
        jest.dontMock('./dog'); // mock を利用しない

        const { Breeder } = await import('./breeder');
        const b = new Breeder();
        b.Show();
    });
  • import() の代わりに require() も利用可能

参考

以下の情報を参考にしました🙇‍♂️

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0