ホームページ > 記事 > ウェブフロントエンド > Jest の概要: 単体テスト、モック、および非同期コード
Jest は JavaScript コードをテストするためのライブラリです。
これは Facebook によって管理されているオープンソース プロジェクトであり、React コードのテストに特に適していますが、これに限定されません。あらゆる JavaScript コードをテストできます。その強みは次のとおりです:
export default function sum(a, n) { return a + b; }
divide.test.js
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
マッチャーは、値をテストできるメソッドです。
依存関係とは、アプリケーションが依存するコードの一部です。それはプロジェクト内の関数/オブジェクト、またはサードパーティの依存関係 (ex axios) である可能性があります
コードがないとアプリケーションが機能できない場合、そのコードは真の依存関係になります。
たとえば、電子メールを送信したり、API リクエストを行ったり、構成オブジェクトを構築したりする機能をアプリケーションに実装する場合
js プロジェクトのコードに依存関係を追加するには、2 つの方法があります。
export default function sum(a, n) { return a + b; }
単純な概念を表す単なる派手な用語です。
関数が外部依存関係からの機能を必要とする場合は、それを引数として挿入するだけです。
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
単体テストは、アプリケーションのセクション (「ユニット」と呼ばれる) が設計を満たし、意図したとおりに動作することを確認するために、ソフトウェア開発者によって作成および実行されます。
コードを分離してテストしたいので、依存関係の実際の実装は気にしません。
検証したい
そこで、依存関係を嘲笑することが重要になります。
単体テストでは、モックは依存関係によって提供される機能をスタブ化する機能と、コードが依存関係とどのように相互作用するかを観察する手段を提供します。
モックは、依存関係をテストに直接組み込むのが高価であったり非現実的である場合、たとえば、コードが API への HTTP 呼び出しを行ったり、データベース層と対話したりする場合
などに特に役立ちます。これらの依存関係に対する応答は、必要に応じて呼び出されるようにしながら、スタブアウトすることが望ましいです。ここでモックが役に立ちます。
モック関数を使用すると、次のことがわかります:
モック関数を作成するにはいくつかの方法があります。
jest.fn メソッドは、それ自体が高階関数です。
これは、新しい未使用のモック関数を作成するファクトリ メソッドです。
JavaScript の関数は第一級市民であり、引数として渡すことができます。
各モック関数にはいくつかの特別なプロパティがあります。モック プロパティは基本です。このプロパティは、関数がどのように呼び出されたかに関するすべての疑似状態情報を含むオブジェクトです。このオブジェクトには 3 つの配列プロパティが含まれています:
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
このタイプのモックは、いくつかの理由からあまり一般的ではありません。
より一般的なアプローチは、jest.mock を使用してモジュールのすべてのエクスポートをモック関数に自動的に設定することです。
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
メソッドが呼び出されるのを監視するだけで、元の実装は保持したい場合があります。また、実装をモックしたい場合もありますが、後でスイート内でオリジナルを復元します。
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
元の実装を復元します
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
JavaScript はシングルスレッドです: 一度に実行できるタスクは 1 つだけです。通常、これは大したことではありませんが、30 秒かかるタスクを実行していると想像してください。そうです。そのタスク中は、何か他のことが起こるまで 30 秒待機します (JavaScript はデフォルトでブラウザのメイン スレッドで実行されます)。そのため、UI 全体が動かなくなってしまいます)。
2020 年、遅くて応答しない Web サイトを望んでいる人はいません。
幸いなことに、ブラウザは、JavaScript エンジン自体が提供していないいくつかの機能、Web API を提供します。これには、DOM API、setTimeout、HTTP リクエスト などが含まれます。これは、非同期、非ブロック動作
を作成するのに役立ちます。
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
Jest は通常、テストの関数を同期的に実行することを想定しています。
非同期操作を実行しても、テストが終了するまで待機する必要があることを Jest に知らせないと、誤検知が発生します。
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
非同期パターン
JavaScript で非同期操作を処理するには、いくつかのパターンがあります。最もよく使用されるものは次のとおりです:
Jest はテストを実行しないため、コールバック内にテストを含めることはできません。テスト ファイルの実行は、コールバックが呼び出される前に終了します。これを修正するには、パラメータをテスト関数に渡します。これを完了と呼び出すと便利です。 Jest は、done() が呼び出されるまで待機してからテストを終了します。
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
Promise を返す関数を使用して、テストから Promise を返します。
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
Promise を返す関数をテストするには、async/await を使用することもできます。これにより、構文が非常に単純かつ単純になります。
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
以上がJest の概要: 単体テスト、モック、および非同期コードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。