フロントエンドテストフレームワークJestシリーズチュートリアル--Mock Functions(シミュレータ)

12851 ワード

前に書く:


ユニットテストを書くときに最も重要なステップはMockです.私たちは通常、インタフェースに基づいてMockインタフェースの実装を行います.例えば、classの中のメソッドをテストします.このメソッドは外部のインタフェースの実装に依存しています.ユニットテストの観点から、私はテストのメソッドの内部論理にしか関心を持っていません.私は現在のclass自体に依存している実装には関心を持っていません.したがって、我々は通常、特定の方法に重点を置いているため、Jestでも同様にMockの機能を提供しています.本節では、JestのMock Functionの機能について説明します.

JestのMock Function


Mock関数は、コード間の接続を簡単にテストすることができます.これは、関数の実際の実装を消去することによって、関数に対する呼び出し(およびこれらの呼び出しで伝達されたパラメータ)をキャプチャし、newを使用してインスタンス化したときに構造関数のインスタンスをキャプチャしたり、テスト時に戻り値を設定できるようにしたりすることによって実現されます.Jestには、Jestが提供するMock Functionを利用して作成する方法と、手動で作成して自身の依存実装を上書きする方法の2つがあります.
関数forEachの内部実装をテストすると仮定します.この関数は、入力された配列の各要素にコールバック関数を呼び出します.コードは次のとおりです.
function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

この関数をテストするには、mock関数を使用して、mock関数のステータスを確認して、コールバック関数が予定通りに呼び出されることを確認します.
const mockCallback = jest.fn();
forEach([0, 1], mockCallback);

//  
expect(mockCallback.mock.calls.length).toBe(2);

//   0
expect(mockCallback.mock.calls[0][0]).toBe(0);

//   1
expect(mockCallback.mock.calls[1][0]).toBe(1);

ほとんどのMock Functionが持っています.mockのプロパティ.この関数が呼び出された情報を保存します..mockプロパティは、呼び出しのたびにthisの値を追跡するので、thisの値をチェックすることも可能になります.
const myMock = jest.fn();

const a = new myMock();
const b = {};
const bound = myMock.bind(b);
bound();

console.log(myMock.mock.instances);

これらのmockメンバー変数は、テストで関数がどのように呼び出されるか、またはインスタンス化されるかを断言する必要がある場合に役立ちます.
//  
expect(someMockFunction.mock.calls.length).toBe(1);

//   arg   'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg');

//   arg   'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg');

//  
expect(someMockFunction.mock.instances.length).toBe(2);

//  ,  name  ,  'test’ 
expect(someMockFunction.mock.instances[0].name).toEqual('test');

Mock関数は、テスト中にテスト値をコードに注入するためにも使用できます.
const myMock = jest.fn();
console.log(myMock());
// > undefined

myMock
  .mockReturnValueOnce(10)
  .mockReturnValueOnce('x')
  .mockReturnValue(true);

console.log(myMock(), myMock(), myMock(), myMock());

関数連続伝達スタイル(CPS)用のコードでは,Mock関数も非常に有効である.このスタイルで記述されたコードは、複雑な中間値を介して実際のコンポーネントでの動作を再構築する必要があることを回避するのに役立ち、呼び出される前にテストに値を直接注入するのに役立ちます.
const filterTestFn = jest.fn();

// Make the mock return `true` for the first call,
// and `false` for the second call
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);

const result = [11, 12].filter(filterTestFn);

console.log(result);
// > [11]
console.log(filterTestFn.mock.calls);
// > [ [11], [12] ]

ほとんどの現実世界の例は、技術的に(上記の説明と同様に)mock関数を用いて依存するコンポーネントを代替して構成することに関連している.これらの場合、本当にテストしたい関数ではない論理を実現することはできるだけ避けます.
指定した戻り値を超える機能が有用であり,シミュレーション関数の実装を全面的に置き換える場合がある.
const myMockFn = jest.fn(cb => cb(null, true));

myMockFn((err, val) => console.log(val));
// > true

myMockFn((err, val) => console.log(val));
// > true

シミュレーションの関数を定義する必要がある場合は、別のモジュールから作成されたデフォルトのインプリメンテーション、mockImplementationメソッドが便利です.
// foo.js
module.exports = function() {
  // some implementation;
};

// test.js
jest.mock('../foo'); // this happens automatically with automocking
const foo = require('../foo');

// foo is a mock function
foo.mockImplementation(() => 42);
foo();
// > 42

複雑な動作のシミュレーション機能を再作成し、複数の関数呼び出しが異なる結果を生成する必要がある場合は、mockImplementationOnceメソッドを使用します.
const myMockFn = jest
  .fn()
  .mockImplementationOnce(cb => cb(null, true))
  .mockImplementationOnce(cb => cb(null, false));

myMockFn((err, val) => console.log(val));
// > true

myMockFn((err, val) => console.log(val));
// > false

指定したmockImplementationOnceの実行が完了すると、デフォルトの被jestが実行する.fn定義のデフォルト実装は、既に定義されていることを前提としている.
const myMockFn = jest
  .fn(() => 'default')
  .mockImplementationOnce(() => 'first call')
  .mockImplementationOnce(() => 'second call');

console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
// > 'first call', 'second call', 'default', 'default'

通常のリンクのある方法(したがって、常にthisを返す必要がある)の場合、文法糖のAPIは.mockReturnThis()関数の形で簡略化され、すべてのシミュレータにも存在する.
const myObj = {
  myMethod: jest.fn().mockReturnThis(),
};

// is the same as

const otherObj = {
  myMethod: jest.fn(function() {
    return this;
  }),
};

また、Mock Functionに正確な名前を付けることもできます.これにより、テストエラー時に出力ウィンドウを特定のFunctionに位置決めするのに役立ちます.
const myMockFn = jest
  .fn()
  .mockReturnValue('default')
  .mockImplementation(scalar => 42 + scalar)
  .mockName('add42');

最後に、mock関数の呼び出し方法をより簡単に説明するために、カスタムマッチング関数を追加しました.
// The mock function was called at least once
expect(mockFunc).toBeCalled();

// The mock function was called at least once with the specified args
expect(mockFunc).toBeCalledWith(arg1, arg2);

// The last call to the mock function was called with the specified args
expect(mockFunc).lastCalledWith(arg1, arg2);

// All calls and the name of the mock is written as a snapshot
expect(mockFunc).toMatchSnapshot();

これらのマッチング器は本当に文法糖の一般的な形態の検査.mock属性にすぎない.あなたはいつも手動で自分の好みに合っている場合、あるいはもっと具体的なことをする必要がある場合:
 
// The mock function was called at least once
expect(mockFunc.mock.calls.length).toBeGreaterThan(0);

// The mock function was called at least once with the specified args
expect(mockFunc.mock.calls).toContain([arg1, arg2]);

// The last call to the mock function was called with the specified args
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
  arg1,
  arg2,
]);

// The first arg of the last call to the mock function was `42`
// (note that there is no sugar helper for this specific of an assertion)
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42);

// A snapshot will check that a mock was invoked the same number of times,
// in the same order, with the same arguments. It will also assert on the name.
expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);
expect(mockFunc.mock.getMockName()).toBe('a mock name');

最後に書きます。


この文書では、Mock Functionの機能、より完全なマッチングリストについて簡単に説明します.リファレンスドキュメントを参照してください.

チュートリアル:


   1.フロントエンドテストフレームワークJestシリーズチュートリアル--Matchers(マッチング)
   2.フロントエンドテストフレームワークJestシリーズチュートリアル--Asynchronous(非同期コードのテスト)
   3.フロントエンドテストフレームワークJestシリーズチュートリアル--Mock Functions(シミュレータ)
   4.フロントエンドテストフレームワークJestシリーズチュートリアル--Global Functions(グローバル関数)