Jestとのインポート機能を模擬する方法
📣 Notice
This article is part of a series:Testing JavaScript with Jest
To get the most out of this article, I recommend reading the previous article in the series:
依存関係を持つコードの書き込み単位のテストは困難である.これは特に、我々がテストする必要があるすべてのケースをカバーする付属品を思い付くためにトリッキーな大きなコードベースの場合はtrueです.
しかし、関数依存の戻り値を制御することができれば、どんな引数が呼び出されてもどうですか?
これはmock関数が入るところです.
MOCK関数は、関数依存性が呼び出され、返り値を制御する方法を追跡するテストツールです.これにより、テストプログラムの制御フローを操作することができ、テストを書く際にそれらの困難なエッジの場合にも到達できます.
本論文では、モッキングの背後にある概念と、それがどのように単体テストに関連するかを紹介する.私たちはどのように機能し、Jestを使用して関数のインポートモジュールを学び、それらのテストケースのカバレッジを高めるためにそれらの模擬に依存してテストを書く.
検証規則のいくつかをテストしていると仮定します.
// isInteger.js
module.exports = (value) => Number.isSafeInteger(value);
// isAtLeast18.js
const isInteger = require("./isInteger");
module.exports = (value) => isInteger(value) && value >= 18;
我々のテストがテストケースを通過して、失敗することによって我々のコードの欠陥について我々に教えるものを見たいです.実装を修正することは、この記事ではカバーされていないが、私たちが記事を移動すると、それを再生する自由を感じる.詳細を見つけるために読んでください!
どのようにJestとインポート機能を模擬するには?
Jestを使ってインポート機能を模擬するには
jest.mock()
function .jest.mock()
が必要な引数で呼び出されます.オプションの2番目の引数- mockのファクトリ関数で呼び出すこともできます.ファクトリ関数が提供されていない場合、Jestはimportモジュールをautomockします.💡Note
Jest automock is the automatic mocking of imported modules with surface-level replacement implementations. Automocking is disabled by default since Jest 15, but can be enabled by configuring Jest with theautomock
flag.
テスト時
isAtLeast18()
我々はそのことを心に留めなければならないisInteger()
依存関係はモジュールの振る舞いに影響を及ぼします:isInteger()
is false
, isAtLeast18()
is false
; isInteger()
is true
, isAtLeast18()
値引数に依存します.isInteger()
帰着false
. The
isInteger.js
モジュールは単一のデフォルトエクスポートを行いますisInteger()
関数.インポートされたモジュールをデフォルトのエクスポートと同じように動作し、関数を返すファクトリ関数を使用します.この関数は、コールされると常にfalse
.// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
// The mock factory returns the function () => false
jest.mock("./isInteger", () => () => false);
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
// Should pass, but fails because of the isInteger() mock
expect(isAtLeast18(123)).toBe(false);
// Should fail either way
expect(isAtLeast18("abc")).toBe(false);
});
});
💡Note
The import path of the mocked module must match the import path that is present in the module we're testing. TheisAtLeast18.js
module imports theisInteger.js
module under the path "./isInteger". This is why our mock import path is also "./isInteger".
isAtLeast18()
は常にfalse
何を私たちがそれを呼び出してもisInteger()
MOCKは常に戻るように設定されますfalse
.しかし、ときはどうですか
isInteger()
リターンtrue
?テストによって異なる戻り値を模擬するには、mock関数を作成します.
💡Note
This unit test is a solitary unit test because the tested unit is isolated from its dependencies. Read more about solitary unit tests in the previous article: .
モック関数とは
mock関数は、関数の実際の実装を「偽」(mock)実装で置き換える機能です.
mock関数は外部コードでどのように呼び出されるかを追跡します.mock関数を使用すると、関数が呼び出された回数、引数が呼び出された引数、返された結果、および多くを知ることができます.関数呼び出しで「スパイ」するこの機能は、なぜmock関数もスパイと呼ばれます.
私たちはカスタムモックの実装を使用して元の関数の動作をオーバーライドするためにmock関数を使用します.模擬実装では、関数の戻り値を制御できます.これは、我々のテストをより予測可能(決定的)にし、書くことが容易になります.
どのようにジェストと機能を模擬するには?
Jestで機能を模擬するには
jest.fn()
function .jest.fn()
実装関数を任意の引数として呼び出すことができます.実装が提供されるならば、MOCK機能を呼ぶことは実装を呼び出して、それが戻り値であることを返します.実装が全く提供されないならば、mock
undefined
返り値が定義されていないためです.// Without implementation, this mock returns `undefined`.
const mockUndefined = jest.fn();
// With implementation, this mock returns `true`.
const mockTrue = jest.fn(() => true).
jest register mockはデフォルトで"jest . fn () "nameの下で機能します.私たちはモック関数にカスタム名を与えることができますmockName()
method . mock nameはテスト結果を印刷する際に使用されます.const mockOne = jest.fn(() => false);
// Example error: expect(jest.fn()).toHaveBeenCalledWith(...expected)
const mockTwo = jest.fn(() => false).mockName('mockTwo');
// Example error: expect(mockTwo).toHaveBeenCalledWith(...expected)
💡Note
It's good practice to name mocked functions in cases where a lot of different mocks are used. This makes it easier to tell mocked functions apart and debug code that isn't matching expectations.
Jestで機能の模擬実装を変える方法?
Jestで機能の模擬実装を変更するには
mockImplementation()
method モンク関数の.The
mockImplementation()
メソッドは新しい実装を引数として呼び出されます.その後、モックが呼ばれるとき、新しい実装は前のものの代わりに使われます.// The initial mock is a function that returns `true`.
const myMock = jest.fn(() => true);
// The new mock implementation has the function return `false`.
myMock.mockImplementation(() => false);
これを組み合わせることができますjest.mock()
工場関数はmockked関数を含むmockkedモジュールを作成します.この方法では、我々がテストしているものによって、モックの実装がどのように振る舞うかを制御することができます.// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
const isInteger = require("./isInteger");
// The mock factory returns a mocked function
jest.mock("./isInteger", () => jest.fn());
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
// For this test we'll mock isInteger to return `false`
isInteger.mockImplementation(() => false);
expect(isAtLeast18(123)).toBe(false);
expect(isAtLeast18("abc")).toBe(false);
});
it("passes if value is recognised as integer and is at least 18", () => {
// For this test we'll mock isInteger to return `true`
isInteger.mockImplementation(() => true);
expect(isAtLeast18(123)).toBe(true);
expect(isAtLeast18("abc")).toBe(false);
});
});
💡Note
jest.mock()
works by modifying the Node module cache to give us the mock instead of the original implementation whenever we import a mocked module in a test file. To support ES module imports - whereimport
statements have to come first in a file - Jest automatically hoistsjest.mock()
calls to the top of the module. Read more about this technique here.
関数がJestで正しく呼び出されたかどうかを確認するには?
Jestを使って関数が正しく呼び出されたかどうかをチェックするには
expect()
function 特定のMatcherメソッドを使用してアサーションを作成します.私たちは
toHaveBeenCalledWith()
matcher method 引数をアサートするには、mockked関数が呼び出されました.mockked関数が呼び出された回数をアサートするには、
toHaveBeenCalledTimes()
matcher method .// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
const isInteger = require("./isInteger");
jest.mock("./isInteger", () => jest.fn());
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
isInteger.mockImplementation(() => false);
expect(isAtLeast18(123)).toBe(false);
// We expect isInteger to be called with 123
expect(isInteger).toHaveBeenCalledWith(123);
// We expect isInteger to be called once
expect(isInteger).toHaveBeenCalledTimes(1);
});
});
💡Note
While these are the most common matcher methods for functions, there are more matcher methods available in the Jest API docs.
Jestはmockked関数へのすべての呼び出しを追跡します.mockked関数はarguments and times これはresults それらの呼び出しの.
テストの間のmockked関数を再利用するとき、それは明確なベースラインを得るために新しいテストを実行する前にそれらの状態をリセットするのに役に立ちます.テストの間のmockkedされた機能をクリアすることによって、それをすることができます.
どのように冗談を使用してmockked機能をクリアするには?
Jestを使ってmockked関数をクリアするには
mockClear()
method モンク関数の.mockClear()
モックアップされた関数に格納されているすべての情報をリセットします.これは、アサーションまたはテストの間の模擬使用データをクリーンアップするのに役立ちます.// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
const isInteger = require("./isInteger");
jest.mock("./isInteger", () => jest.fn());
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
isInteger.mockImplementation(() => false);
expect(isAtLeast18(123)).toBe(false);
expect(isInteger).toHaveBeenCalledWith(123);
expect(isInteger).toHaveBeenCalledTimes(1);
// Clear the mock so the next test starts with fresh data
isInteger.mockClear();
});
it("passes if value is recognised as integer and is at least 18", () => {
isInteger.mockImplementation(() => true);
expect(isAtLeast18(123)).toBe(true);
expect(isInteger).toHaveBeenCalledWith(123);
// Without clearing, there would be 2 calls total at this point
expect(isInteger).toHaveBeenCalledTimes(1);
});
});
💡Note
Jest also provides mock function methods for resetting and restoring mocked functions, as well as shorthands for creating mocked functions that directly return, resolve, or reject a value.
どのようにJestと各テストの前にmockked機能をクリアするには?
Jestで各テストの前にmockked関数をクリアするには
beforeEach()
function .beforeEach()
は必須の引数と呼ばれ、テストファイルのテスト前に実行される.私たちはクリアモックには、フィクスチャを設定するか、またはテストに使用されるいくつかの他の状態をリセットするために使用します.// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
const isInteger = require("./isInteger");
jest.mock("./isInteger", () => jest.fn());
// Clear mock data before each test
beforeEach(() => {
isInteger.mockClear();
});
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
isInteger.mockImplementation(() => false);
expect(isAtLeast18(123)).toBe(false);
expect(isInteger).toHaveBeenCalledWith(123);
expect(isInteger).toHaveBeenCalledTimes(1);
});
it("passes if value is recognised as integer and is at least 18", () => {
isInteger.mockImplementation(() => true);
expect(isAtLeast18(123)).toBe(true);
expect(isInteger).toHaveBeenCalledWith(123);
expect(isInteger).toHaveBeenCalledTimes(1);
});
});
💡Note
Jest provides four functions to hook into the set-up and tear-down process, both before and after each or all of the tests in a test file. These functions are:afterAll()
,afterEach()
,beforeAll()
,beforeEach()
. TheafterAll()
andbeforeAll()
variants are called only once for the entire test file. TheafterEach()
andbeforeEach()
variants are called once for every test in the test file.
どのように冗談を使用してモックを再利用するには?
冗談でもてあそびを再利用するために、我々は
__mocks__/
subdirectory モジュールに隣接して、我々はモックしたいです.モックファイル
__mocks__/
サブディレクトリは、モジュールがモックされたときに隣接するモジュールをautomockするために使用されますjest.mock()
. これは、モックスファクトリ機能を不要にするため、一般的な依存関係やコンフィギュレーションオブジェクトをモッキングするときなどのモックを設定する際に多くの繰り返しを扱うときに便利です.多くの異なるモジュールが使用する一般的な設定ファイルを想定して、以下のようになります.
// common/config.js
module.exports = { foo: "bar" };
// common/__mocks__/config.js
module.exports = { foo: "mockBar" };
// example.js
const config = require.("./common/config");
// Logs "bar"
module.exports = () => console.log(config.foo);
// example.spec.js
const example = require("./example");
jest.mock("./common/config");
// Logs "mockBar", no need for a mock factory
example();
💡Note
Remember: mocks and automocking are only in effect when running tests with Jest. They do not have an effect on the code in development or production.
それだ!我々は現在、Jestとインポート機能を模擬する準備が整いました.
ジェストMOCKインポート機能例コード
依存性
isInteger.js
:// isInteger.js
module.exports = (value) => Number.isSafeInteger(value);
テストする単位isAtLeast18.js
:// isAtLeast18.js
const isInteger = require("./isInteger");
module.exports = (value) => isInteger(value) && value >= 18;
ユニットテストisAtLeast18.spec.js
:// isAtLeast18.spec.js
const isAtLeast18 = require("./isAtLeast18");
const isInteger = require("./isInteger");
// The mock factory returns a mocked function
jest.mock("./isInteger", () => jest.fn());
beforeEach(() => {
isInteger.mockClear();
});
describe("isAtLeast18", () => {
it("fails if value is not recognised as integer", () => {
isInteger.mockImplementation(() => false);
expect(isAtLeast18(123)).toBe(false);
expect(isInteger).toHaveBeenCalledWith(123);
expect(isInteger).toHaveBeenCalledTimes(1);
});
it("passes if value is recognised as integer and is at least 18", () => {
isInteger.mockImplementation(() => true);
expect(isAtLeast18(123)).toBe(true);
expect(isInteger).toHaveBeenCalledWith(123);
expect(isInteger).toHaveBeenCalledTimes(1);
});
});
宿題と次のステップ
あなたは前にJestとインポート機能をモッキングしようとしたことがありますか?あなたの経験は何でしたか?
コメントを残すとディスカッションを開始!
Reference
この問題について(Jestとのインポート機能を模擬する方法), 我々は、より多くの情報をここで見つけました https://dev.to/dstrekelj/how-to-mock-imported-functions-with-jest-3pflテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol