1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Jestでモック関数を使って「イベントが呼び出された」ことを確認する

Last updated at Posted at 2022-06-28

「イベントが呼び出されたかどうか」ということをモック関数を使って確かめる方法に関するメモ。

Jestそのものの導入、操作方法は過去記事参照。

公式ドキュメントは以下。

テスト用のクラスを用意する

以下のようなクラスを用意した。

import { EventEmitter } from "events";
const emitter = new EventEmitter();

export class TestClass extends EventEmitter{
    constructor(){
        super();
    }

    testPing = () => {
        this.emit("testEvent", {"type": "test"});
    }
}

testPing関数を動かすと、testEventイベントが帰ってくる。これが正常に動くかどうか確かめたい。

求めるのは以下のようなテストとなる。

  • testEventが呼び出された際に引数内にtype: "test"が含まれていればテストが通る
  • testEventが呼び出された際に引数内にtype: "test"が含まれていなければテストが通らない
  • testEventが呼び出されなければテストが通らない

ダメな例

以下のようなテストコードを作ってみる。

import {TestClass} from "../src/eventTest";

it("should get event with correct type", () => {
    const testClass = new TestClass();

    testClass.on("testEvent", (msg) => {
        expect(msg).toEqual(
            expect.objectContaining({
              type: "test",
            })
          );        
    });
    testClass.testPing();
});

TestClassのインスタンスを作成し、testPingを動かす。

testEventの発火時にイベントリスナーを動かし、受け取ったメッセージを確認する。ここにtype: "test"というオブジェクトが含まれていれば通る。

これで一見よさそうだが、このまま動かすと以下のようなテストになる。

  • testEventが呼び出された際に引数内にtype: "test"が含まれていればテストが通る
  • testEventが呼び出された際に引数内にtype: "test"が含まれていなければテストが通らない
  • testEventが呼び出されなければテストが通る
    • 本来は通ってはいけない

例えばclassの方を少し書き換えてみる。

    testPing = () => {
        this.emit("testEvent_", {"type": "test"});
    }

testEventではなくtestEvent_が発火するようにした。この場合先ほどのテストは通らないでほしいが...

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total

通ってしまう。 これはイベントの呼び出しの有無を確かめてるわけではないため。

これを正しくテストするために、モック関数というものを使用する。

モック関数って何?

「関数がどのように呼び出されたか」ということを捉える関数。これを用いることで今まで何回その関数が呼び出されたか、どのような引数を持って呼び出されたかを取得することができる。

以下のようにして作成できる。

const testEventListener = jest.fn();

ここから、以下のようなexpect文で呼び出しに関する情報を調べることができる。

// 呼び出されたか否か
expect(testEventListener).toHaveBeenCalled();

// 指定の引数で呼び出されたことがあるか
expect(testEventListener).toHaveBeenCalledWith(arg1, arg2);

// 前回の呼び出しで指定の引数だったか
expect(testEventListener).toHaveBeenLastCalledWith(arg1, arg2);

正しい実装例

というわけで、以下のように実装できる。

import { TestClass } from "../src/eventTest";

it("should get event with correct type", () => {
    const testClass = new TestClass();
    const testEventListener = jest.fn();

    testClass.on("testEvent", (msg) => {
        testEventListener(msg);
    });

    testClass.testPing();

    expect(testEventListener).toHaveBeenCalledWith(
        expect.objectContaining({
            type: "test",
        })
    )
});

これで正常に動くようになる。

イベント発火時にはモック関数を動かすだけのリスナーを登録し、

    testClass.on("testEvent", (msg) => {
        testEventListener(msg);
    });

その後そのモック関数が呼ばれているかを確認した。

    expect(testEventListener).toHaveBeenCalledWith(
        expect.objectContaining({
            type: "test",
        })
    )

これで

  • testEventが呼び出された際に引数内にtype: "test"が含まれていればテストが通る
  • testEventが呼び出された際に引数内にtype: "test"が含まれていなければテストが通らない
  • testEventが呼び出されなければテストが通らない

という条件を満たすことができた。

1
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?