17
6

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 3 years have passed since last update.

[TypeScript] Jestでインスタンスのモックを作る方法

Last updated at Posted at 2020-08-16

テスト対象

gateway.ts
export interface Driver {
  fetch: () => string;
}

export class Gateway {
  constructor(private driver: Driver) {}

  fetch() {
    const text = this.driver.fetch();
    return text;
  }
}

GatewayクラスはコンストラクタにDriverインターフェイスが実装されたインスタンスを受け取ります。
fetch()メソッドで受け取ったインスタンスのfetch()メソッドを更に呼び出します。

実際にGatewayクラスを使う場合は下のようなコードになります。

// Driverインターフェイスを実装したTestDriverクラスを定義
class TestDriver implements Driver {
  fetch() {
    return 'test'
  }
}
const driver = new TestDriver();
// TestDriverクラスのインスタンスをコンストラクタで受け取る
const gateway = new Gateway(driver);
gateway.fetch();

今回はGatewayクラスのfetch()メソッドのテストを書いていきます。

テストコード

fetch()メソッドのテストコードを書いていくと下のようになります。

gateway.spec.ts
import { Driver, Gateway } from './gateway.ts'

it('gateway fetch success', () => {
  const expected = 'test';

  const driver = ????
  const gateway = new Gateway(driver);
  const actual = gateway.fetch();
  expect(actual).toBe(expected)
})

Gatewayのインスタンスを作るときに困ったことが起きます。
GatewayクラスのコンストラクタはDriverインターフェイスのインスタンスを受け取る必要があります。

しかし、gateway.tsにはDriverインターフェイスを実装したクラスは存在しません。
なのでインスタンスを用意することができません。

ここでモックを使うことでインスタンスを擬似的に用意することができます。

インスタンスのモックを実装

モックを実装するにはjest.fn()を使います。

const driverMock = jest.fn<Driver, []>();

ここでfnのgenericsの第一引数にモックするインスタンスの型(インターフェイスなども含む)、第二引数にコンストラクタのパラメータの型を設定します。
今回モックしたいのはDriverのインスタンスなので、第一引数にDriver、第二引数に[](空配列)を設定しています。

これに加えてGatewayクラスが呼び出すDriverのfetch()メソッドの実装をモックに定義する必要があります。
モック
実装を定義するにはmockImplementation()メソッドを使います。

const DriverMock = jest.fn<TestDriver, []>().mockImplementation(() => {
  return {
    fetch: () => {
      return 'test'
    }
  }
})
const driver = new DriverMock();

mockImplementation()メソッドに関数を引数に渡します。
引数に渡す関数はモックに定義したいメソッドを含んだオブジェクトを返すように定義します。
今回はfetch()メソッドを実行すると'test'を返すように定義しています。

最終的にgateway.spec.tsは下のようになります。

gateway.spec.ts
import { Driver, Gateway } from './gateway.ts'

it('gateway fetch success', () => {
  const expected = 'test';

  const DriverMock = jest.fn<TestDriver, []>().mockImplementation(() => {
    return {
      fetch: () => {
        return 'test'
      }
    }
  })
  const driver = new DriverMock();
  const gateway = new Gateway(driver);
  const actual = gateway.fetch();
  expect(actual).toBe(expected)
})

メソッドが呼ばれたかテストする

上のテストコードでは作成したモックのfetch()メソッドが本当に呼ばれているかがテストされていません。
もしかしたらdriverのfetch()メソッドを呼ばずにテストが通ってしまっている可能性もあります。
正しくfetch()が呼ばれるかをテストするテストコードを加えてみましょう。

関数が呼ばれたかどうかをテストするにはexpectのtoBeCalled()メソッドを使います。
expectに関数を渡してtoBeCalled()メソッドを呼ぶことで、expectに渡された関数が実行されたかどうかをテストできます。

expect(function).toBeCalled();

fetch()メソッドが呼ばれたかどうかをテストするテストコードを加えると下のようになります。

gateway.spec.ts
import { Driver, Gateway } from './gateway.ts'

it('gateway fetch success', () => {
  const expected = 'test';

  const fetchFunctionMock = jest.fn(() => {
    return 'test'
  })
  const DriverMock = jest.fn<TestDriver, []>().mockImplementation(() => {
    return {
      fetch: fetchFunctionMock
    }
  })
  const driver = new DriverMock();
  const gateway = new Gateway(driver);
  const actual = gateway.fetch();
  expect(actual).toBe(expected)
  expect(fechFunctionMock).toBeCalled();
})

fetch()メソッドは変数のfetchFunctionに一旦入れています。
変数に入れることでexpect()の引数に渡せるようにしています。

17
6
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
17
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?