1. ホーム
  2. javascript

[解決済み] モックの実装を単体テストごとに変更する方法【Jestjs

2022-05-03 19:28:38

質問

私は、以下のことを望んでいます。 モック化された依存関係の実装を変更する を、その都度 単一テストベース によって デフォルトのモックを拡張する の動作と 元に戻す を、次のテスト実行時に元の実装に戻すことができます。

もっと簡単に言うと、私が実現しようとしているのはこういうことです。

  1. 模擬 依存関係
  2. モック実装の変更・拡張 一つのテストで
  3. 差し戻し 次のテストが実行されたときに元のモックに戻す

現在、私は Jest v21 .

典型的なJestのテストは以下のようなものです。

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);

export default myMockedModule;

__tests__/myTest.js

import myMockedModule from '../myModule';

// Mock myModule
jest.mock('../myModule');

beforeEach(() => {
  jest.clearAllMocks();
});

describe('MyTest', () => {
  it('should test with default mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });

  it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
    // Extend change mock
    myMockedModule.a(); // === true
    myMockedModule.b(); // === 'overridden'
    // Restore mock to original implementation with no side effects
  });

  it('should revert back to default myMockedModule mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });
});

これまで試した内容は以下の通りです。


1 - mockFn.mockImplementationOnce(fn)

プロ

  • 最初の呼び出し後、元の実装に戻る

短所

  • を呼び出すと壊れます。 b 複数回
  • まで、元の実装に戻りません。 b が呼ばれない(次のテストで漏れる)。

のコードになります。

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  myMockedModule.b.mockImplementationOnce(() => 'overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});


2 - jest.doMock(moduleName, factory, options)

プロ

  • すべてのテストで明示的にリモックを行う

短所

  • すべてのテストに対してデフォルトのモック実装を定義することができない
  • デフォルトの実装を拡張するには、モック化された各実装を再宣言する必要があります。 メソッド

のコードで指定します。

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  jest.doMock('../myModule', () => {
    return {
      a: jest.fn(() => true,
      b: jest.fn(() => 'overridden',
    }
  });

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});


3 - セッターメソッドによる手動モッキング(説明の通り こちら )

プロ

  • モック結果に対する完全なコントロール

短所

  • ボイラープレート・コードが多い
  • 長期的なメンテナンスが困難

のコードで構成されています。

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

let a = true;
let b = true;

myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);

myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
  a = true;
  b = true;
};
export default myMockedModule;

__tests__/myTest.js

it('should override myModule.b mock result (and leave the other methods untouched)', () => {
  myModule.__setB('overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'

  myModule.__reset();
});


4 - jest.spyOn(オブジェクト, メソッド名)

コンサ

  • 元に戻すことができない mockImplementation を元のモックの返り値に戻してしまうので、次のテストに影響が出ます。

のコードになります。

beforeEach(() => {
  jest.clearAllMocks();
  jest.restoreAllMocks();
});

// Mock myModule
jest.mock('../myModule');

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');

  myMockedModule.a(); // === true
  myMockedModule.b(); // === 'overridden'

  // How to get back to original mocked value?
});

解決方法は?

テストを書くための良いパターンは、現在のモジュールをテストするために必要なデータを返すセットアップファクトリ関数を作成することです。

以下は、2つ目の例に従ったサンプルコードですが、デフォルト値とオーバーライド値を再利用可能な方法で提供することができます。


const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    jest.doMock('../myModule', () => mockedFunctions)
    return {
      mockedModule: require('../myModule')
    }
  }

  it("should return true for module a", () => {
    const { mockedModule } = setup();
    expect(mockedModule.a()).toEqual(true)
  });

  it("should return override for module a", () => {
    const EXPECTED_VALUE = "override"
    const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
    expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
  });
});

重要なのは、あなたが なければならない を使用してキャッシュされたモジュールをリセットします。 jest.resetModules() . これは beforeEach または同様のティアダウン機能

詳しくは、jestオブジェクトのドキュメントをご覧ください。 https://jestjs.io/docs/jest-object .