単一のテストごとにモック実装を変更する方法[Jestjs]


96

デフォルトのモックの動作を拡張し、次のテストの実行時に元の実装に戻すことで単一のテストごとにモックされた依存関係の実装変更したいと思います

もっと簡単に言えば、これは私が達成しようとしていることです:

  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(object、methodName)

短所

  • 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?
});

いいね。しかし、「@ private-repo / module」のようなnpmモジュールのオプション2をどのように実行しますか?私が見るほとんどの例には相対パスがありますか?これはインストールされたモジュールでも機能しますか?
mrbinky30 0019年

回答:


54

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

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

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

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

  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)
  });
});

42

バニラJS

mockFn.mockImplementation(fn)を使用します。

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});

TypeScript

バニラJSソリューションに加えて:

メッセージmockImplementationがfuncToMockのプロパティではないことを防ぐには、タイプを指定する必要があります。たとえば、一番上の行を上から次のように変更します。

import { (funcToMock as jest.Mock) } from './somewhere';

この問題に対処する質問はここにあります:jesttypescriptプロパティモックはタイプに存在しません


23

パーティーに少し遅れましたが、他の誰かがこれに問題を抱えている場合。

反応ネイティブ開発には、TypeScript、ES6、およびbabelを使用します。

通常、ルート__mocks__ディレクトリにある外部NPMモジュールをモックします。

特定のテストのために、aws-amplifyのAuthクラスのモジュールの特定の機能をオーバーライドしたかったのです。

    import { Auth } from 'aws-amplify';
    import GetJwtToken from './GetJwtToken';
    ...
    it('When idToken should return "123"', async () => {
      const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
        getIdToken: () => ({
          getJwtToken: () => '123',
        }),
      }));

      const result = await GetJwtToken();
      expect(result).toBe('123');
      spy.mockRestore();
    });

要旨:https//gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

チュートリアル:https//medium.com/p/b4ac52a005d#19c5

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.