ホームページ >ウェブフロントエンド >jsチュートリアル >Jest の概要: 単体テスト、モック、および非同期コード

Jest の概要: 単体テスト、モック、および非同期コード

Linda Hamilton
Linda Hamiltonオリジナル
2024-11-01 00:23:28678ブラウズ

Introduction to Jest: Unit Testing, Mocking, and Asynchronous Code

ジェストの紹介

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

マッチャー

マッチャーは、値をテストできるメソッドです。

  • toBe は === を使用して厳密な等価性を比較します。
  • toEqual は 2 つの変数の値を比較します。オブジェクトまたは配列の場合は、すべてのプロパティまたは要素が等しいかどうかをチェックします
  • null 値を渡す場合、toBeNull は true になります
  • 定義された値を渡す場合、toBeDefined は true になります (上記とは逆)
  • 未定義の値を渡す場合は toBeUnknown が true になります
  • toBeCloseTo は浮動小数点値を比較するために使用され、丸めエラーを回避します
  • toBeTruthy 値が true とみなされる場合は true (if と同様)
  • toBeFalsy 値が false とみなされる場合は true (if と同様)
  • Expect() の結果が引数
  • より大きい場合は toBeGreaterThan true
  • toBeGreaterThanOrEqual Expect() の結果が引数と等しいか、引数より大きい場合は true
  • Expect() の結果が引数
  • より小さい場合は toBeLessThan true
  • toBeLessThanOrEqual Expect() の結果が引数と等しいか、引数より小さい場合は true
  • toMatch は、正規表現パターン マッチングで文字列を比較するために使用されます
  • toContain は配列で使用されます。期待される配列の要素に引数が含まれている場合は true、set
  • toHaveLength(number): 配列の長さをチェックします
  • toHaveProperty(key, value): オブジェクトにプロパティがあるかどうかを確認し、オプションでその値を確認します
  • toThrow は、渡した関数が例外 (一般) または特定の例外をスローするかどうかをチェックします
  • toBeInstanceOf(): オブジェクトがクラスのインスタンスであるかどうかを確認します

依存関係

依存関係とは、アプリケーションが依存するコードの一部です。それはプロジェクト内の関数/オブジェクト、またはサードパーティの依存関係 (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 呼び出しを行ったり、データベース層と対話したりする場合

などに特に役立ちます。

これらの依存関係に対する応答は、必要に応じて呼び出されるようにしながら、スタブアウトすることが望ましいです。ここでモックが役に立ちます。

モック関数を使用すると、次のことがわかります:
  • 受信した通話数
  • 各呼び出しで使用される Argument
  • の値。
  • 各呼び出しの 「コンテキスト」またはこの
  • 値。
  • 関数がどのように終了したか、およびどのような値が生成されたか

冗談で嘲笑する

モック関数を作成するにはいくつかの方法があります。
  • jest.fn メソッドを使用すると、新しいモック関数を直接作成できます。
  • オブジェクト メソッドをモックしている場合は、jest.spyOn を使用できます。
  • モジュール全体をモックしたい場合は、jest.mock を使用できます。

jest.fn メソッドは、それ自体が高階関数です。

これは、新しい未使用のモック関数を作成するファクトリ メソッドです。

JavaScript の関数は第一級市民であり、引数として渡すことができます。

各モック関数にはいくつかの特別なプロパティがあります。モック プロパティは基本です。このプロパティは、関数がどのように呼び出されたかに関するすべての疑似状態情報を含むオブジェクトです。このオブジェクトには 3 つの配列プロパティが含まれています:
  • 呼び出し [各呼び出しの引数]
  • インスタンス [各呼び出しの「この」値]
  • Results [関数が終了した値]、results プロパティには型 (return または throw) と値があります。
    • 関数は明示的に値を返します。
    • 関数は return ステートメントなしで完了まで実行されます (これは、未定義を返すのと同じです
    • 関数はエラーをスローします。
export default function sum(a, n) {
  return a + b;
}
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b

モックベーシック

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';

モジュールのモック化

jest.fn を使用して関数をモックする

// 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 はモジュール内のすべての関数に対してこれを自動的に実行します
  • jest.spyOn も同じことを行いますが、元の関数を復元できます

jest.mock でモジュールをモックする

より一般的なアプローチは、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;
}

jest.spyOn を使用して関数をスパイまたはモックする

メソッドが呼び出されるのを監視するだけで、元の実装は保持したい場合があります。また、実装をモックしたい場合もありますが、後でスイート内でオリジナルを復元します。

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 とイベント ループ

JavaScript はシングルスレッドです: 一度に実行できるタスクは 1 つだけです。通常、これは大したことではありませんが、30 秒かかるタスクを実行していると想像してください。そうです。そのタスク中は、何か他のことが起こるまで 30 秒待機します (JavaScript はデフォルトでブラウザのメイン スレッドで実行されます)。そのため、UI 全体が動かなくなってしまいます)。
2020 年、遅くて応答しない Web サイトを望んでいる人はいません。

幸いなことに、ブラウザは、JavaScript エンジン自体が提供していないいくつかの機能、Web API を提供します。これには、DOM APIsetTimeoutHTTP リクエスト などが含まれます。これは、非同期非ブロック動作
を作成するのに役立ちます。

export default function sum(a, n) {
  return a + b;
}
  • 呼び出しスタック - 関数を呼び出すと、関数は呼び出しスタックと呼ばれるものに追加されます。
  • WebAPI - setTimeout は WebAPI によって提供され、コールバック関数を受け取り、メインスレッドをブロックせずにタイマーを設定します
  • キュー - タイマーが終了すると、コールバックがキュー
  • に追加されます。
  • Event Loop - コールスタックが空かどうかを確認し、キュー内に実行するコールバックがあるかどうかを確認し、実行するコールスタックに移動します。
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 は通常、テストの関数を同期的に実行することを想定しています。

非同期操作を実行しても、テストが終了するまで待機する必要があることを 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");
});

ヒント

  • 関数が何を行うのか、何をテストしようとしているのかをよく理解する必要があります
  • テストしているコードの動作について考えてみましょう
  • 舞台を設定します:
    • 依存関係をモック/スパイします
    • コードはグローバル オブジェクトと対話していますか?私たちも彼らを嘲笑したりスパイしたりすることができます
    • テストは DOM と対話していますか?いくつかの偽の要素を構築して動作させることができます
    • テストを構造化する
    • 与えられた ...
    • 電話すると....
    • それでは...期待しています....
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);
});

リンク

  • https://medium.com/@rickhanlonii/ Understanding-jest-mocks-f0046c68e53c
  • https://jestjs.io/docs/en/mock-functions
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b
  • https://github.com/BulbEnergy/jest-mock-examples
  • https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
  • https://jestjs.io/docs/en/asynchronous
  • https://www.pluralsight.com/guides/test-asynchronous-code-jest

以上がJest の概要: 単体テスト、モック、および非同期コードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。