JavaScript 単体テストのために Jest を始める方法

Jest は、「単体テスト」を実行するためのフレームワークです。これは、コードベースの個々の関数とコンポーネントをテストして、今後のコミットで予期しない問題が発生しないことを確認するための方法です。これを設定する方法と、React などのフロントエンド フレームワークで使用する方法を示します。
単体テストとは何ですか?
プログラマーのチームと作業する場合、テストは非常に重要です。ソース管理にプッシュされたコミットはすべて自動的にビルドされ、テストされて、誤って何かを壊すことがないようにする必要があります。全員を正気に保つために、パイプライン全体を Jenkins などのソフトウェアで自動化できますが、実際のテストを実行するには、Jest のような単体テスト フレームワークを使用する必要があります。
Jest は、「単体テスト」を実行するためのフレームワークです。 「ユニット」には、関数、クラス、コンポーネントなど、さまざまなものを指定できます。これは、将来のコードの更新によって何も壊れないことを確認するために、テストする必要があるコードの最小単位を単に表します。エンドポイントからデータを取得して出力を返す関数を作成する場合は、この関数を受け取り、呼び出して出力を記録する、対応する単体テストを作成します。その後、返された内容に基づいてアサーションを行うことができます。正しくフォーマットされていますか?未定義の値はありますか?期待どおりに動作していますか?これらのテストのいずれかが失敗した場合は、コードに何か問題があります。
Jest は、Web アプリケーションでテストを実行するために使用することもできます。これは、コンポーネントが特定の入力に対して適切な状態と props を持っていることを保証するために、React のようなデータ駆動型フレームワークでよく使用されます。
単体テストを作成するのは面倒に思えるかもしれませんし、確かに手間がかかりますが、通常は最終的により良いコードベースにつながります。レガシー コードを扱うことが問題になるのはまさにこの理由からです。適切なコメントを残すことは役立ちますが、単体テストを書くことは関数のドキュメントを書くことに似ています。どのような入力が入力されるか、その使用方法、およびどのような入力が期待されるかを定義します。
Jest の入門
単純なテスト環境をセットアップするには、npm init -y
を使用して新しいプロジェクトを作成し、npm
から開発依存関係として Jest をインストールします。
npm install --save-dev jest
コマンドラインからテストを実行するには、Jest を呼び出す簡単なスクリプトを package.json
に追加する必要があります。
"scripts": {
"test": "jest"
}
テストする関数が必要なので、src/doSomeMath.js
の下に新しいファイルを作成し、関数をエクスポートするように設定します。
function doSomeMath(a, b) {
return a + b;
}
module.exports = doSomeMath;
このファイルを保存し、tests/doSomeMath.test.js
を開きます。 Jest は自動的に test/
ディレクトリを調べて、実行するテストを見つけます。このファイルは、doSomeMath.js
のコンパニオン ファイルとして機能し、すべての関数が適切に動作していることを確認するための一連のテストを定義します。基本的な Jest テストは次のようになります。
test('Description', () => {
expect(functionName(args)).toBe(result);
});
基本的に、test()
関数はテスト用のすべてのコードをラップします。各 expect()
ブロックは関数 (つまり、ユニット) を呼び出し、その値を「マッチャー」に渡します。この場合、それは単純な等価性をチェックする toBe()
関数です。
Jest にはたくさんのさまざまなマッチャーがあり、ドキュメントで詳しく読むことができます。いくつか例を挙げると、
.not.matcher()
はマッチャーの反転を処理します。toBe()
は完全に等しいかどうか (Object.is) をチェックしますが、特にオブジェクトは処理しません。toEqual()
はオブジェクトの詳細な同等性をチェックします。toStrictEqual()
はtoEqual
と同じですが、どちらのオブジェクトにも追加の未定義プロパティがないことを確認します。toBeTruthy()
とtoBeFalsy()
は、< でtrue
またはfalse
と評価されるものがないかどうかをチェックします。if ステートメント。-
toBeNull()
、toBeUndefined()
、およびtoBeDefined()
はすべて、オブジェクトのさまざまな状態をチェックします。 toContain()
は配列の内容をチェックします。toMatch()
は正規表現を文字列と照合します。toThrow()
は、コードがエラーをスローすることを確認します(通常は間違った入力をテストするため)。
このテスト スイートでは、関数をインポートし、いくつかの引数と結果を渡す必要があります。複数のシナリオに対応するために、1 つのテスト内に複数の expect
ブロックを含めることができますし、通常はそうする必要があります。
const doSomeMath = require('../src/doSomeMath');
test('adds 2 + 2 to equal 4', () => {
expect(doSomeMath(1, 1)).toBe(2);
expect(doSomeMath(2, 2)).toBe(4);
});
このファイルを保存して、次のコマンドを実行します。
npm run test
これにより、すべてのテスト スイートが実行され、それぞれの結果が出力されます。

数学が正しく機能していれば、すべてが成功するはずです。失敗した場合、Jest は問題の原因を詳細に説明し、問題の追跡に役立ちます。
Jest コードは単なる JavaScript であるため、テストのスクリプトを非常に簡単に作成できます。たとえば、for ループを使用して、反復入力で expect
を複数回呼び出すことができます。 expect
の実行が失敗すると、テスト自体も失敗します。
test('adds 2 + 2 to equal 4', () => {
for (let a = 1; a < 10; a++) {
expect(doSomeMath(a, 5)).toBe(a + 5)
}
});
この機能はさまざまな目的に使用できますが、ほとんどのテストでは通常、次の 3 つの主要なことが行われます。
- ユニット用にデータを準備する配置
- ユニットが呼び出される Act には、配置されたデータが渡され、出力がログに記録されます
- アサート。すべてのマッチャーを実行して、すべてが意図したとおりに動作していることを確認します
React を使ったテスト
数学が機能することを確認するのは素晴らしいことですが、おそらく、そのような単純なことのために Jest を使用するつもりはありません。おそらく、Jest を使用して、React などのフレームワークで構築された JavaScript アプリケーションのテストを自動化することに興味があるでしょう。手動レビューを使用して完全な UI テストを行う必要がありますが、Jest を使用して React コンポーネントを分離してテストすることもできます。結局のところ、React コンポーネントは実際には JSX 出力を返す単なる関数であり、これは照合してテストすることができます。
まず、create-react-app
を使用して新しい React プロジェクトをセットアップします。
npx create-react-app jest-react
Create React App では、デフォルトで Jest がインストールされます。また、Jest で React を操作するための便利なハンドラーが含まれる React Testing Library もインストールされます。
デフォルトの App.js
コンポーネントを次のように変更します。
import React, {useState} from 'react';
import './App.css';
function App() {
const [disabled, setDisabled] = useState(false);
function handleClick() {
setDisabled(!disabled);
}
return (
<button data-testid="useless-button" disabled={disabled} onClick={handleClick}>
Click Me
</button>
);
}
export default App;
このまったく役に立たないコード部分は、一度クリックされると、このボタンのそれ以降のクリックをすべて無効にするために使用される状態変数を設定するボタンを定義しています。
App.test.js
を開きます。これは、Create React App によってすでに作成されており、デフォルトの React ロゴのテストが含まれています。代わりに、デフォルトのテストを次のように変更します。
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from './App';
test('disabled button on click', () => {
const button = render(<App />).getByTestId('useless-button');
expect(button.disabled).toBeFalsy();
fireEvent.click(button, {button: 1});
expect(button.disabled).toBeTruthy();
});
このテストは、React Testing Library のレンダリングを使用してコンポーネントをレンダリングし、指定したテスト ID に基づいてボタンを見つけます(ただし、コンポーネント コードを乱雑にしないさまざまなセレクターが多数あります)。ボタンが最初から無効になっていないことを確認してから、クリック イベントを発生させます。 ({button: 1}
は単に左クリック用のコードであることに注意してください。)状態が更新され、再レンダリングがトリガーされ、ボタンが無効になっていることを再度確認します。
関数の出力を常にテストすることが重要です。ほとんどの場合、関数が内部でどのように実装されているかは正確には気にせず、関数が正しい出力を返すことだけを気にします。このコンポーネントは、無効になる場合と無効にならない場合があるボタンを返す単なる関数です。ボタンが内部で React の状態 API を使用しているかどうかは気にしないので、返された HTML に無効なボタンが含まれているかどうかをテストします。状態をテストできないというわけではありませんが、必要かどうかを検討する必要があります。