JavaScriptユニットテストのためのJestの始め方
Jestは「ユニットテスト」を実行するためのフレームワークであり、これはコードベースの個々の関数やコンポーネントをテストして、将来のコミットが予期しない何かを壊さないことを保証する方法です。セットアップ方法と、Reactのようなフロントエンドフレームワークと一緒に使用する方法をお見せします。
ユニットテストとは何ですか?
チームのプログラマーと働くとき、テストは非常に重要です。ソース管理にプッシュされたすべてのコミットは、自動的にビルドおよびテストされ、何も壊さないようにする必要があります。皆が冷静でいられるように、全体のパイプラインはJenkinsのようなソフトウェアで自動化できますが、実際のテストを実行するには、Jestのようなユニットテストフレームワークを使用する必要があります。
Jestは「ユニットテスト」を実行するためのフレームワークです。「ユニット」は、関数、クラス、コンポーネントなど、あらゆるものを指すことができ、テストすべきコードの最小単位を表します。これは、今後のコードの更新が何かを壊さないようにするためです。もしデータをエンドポイントから取得しいくつかの出力を返す関数を書く場合、それに対応するユニットテストを書き、この関数を呼び出して出力をログに記録します。そして、それが返したものに基づいて主張を行うことができます。それは正しくフォーマットされていますか?未定義の値はありますか?期待通りに動作していますか?これらのテストのいずれかが失敗した場合、あなたのコードに何か問題があります。
Jestはウェブアプリケーションのテストを実行するためにも使用できます。これは、Reactのようなデータ駆動型フレームワークと一般的に使用され、コンポーネントが与えられた入力に対して適切な状態とプロパティを持っていることを確認します。
単体テストを書くのは面倒に思えるかもしれませんが、確かに手間がかかりますが、最終的にはより良いコードベースに繋がることが多いです。レガシーコードの扱いが問題になるのはまさにこの理由です---適切なコメントを残すことは助けになりますが、単体テストを書くことは関数のドキュメントを書くのと同じです。どの入力が必要か、どのように使うか、そしてどのような出力が期待できるかを定義します。
Jestの使い方
簡単なテスト環境を設定するには、新しいプロジェクトを作成します
npm init -y、そして、Jestを開発依存関係としてインストールします
npm:
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()は、if文でtrueまたはfalseと評価されるものを確認します。toBeNull()、toBeUndefined()、およびtoBeDefined()は、すべてオブジェクトの異なる状態をチェックします。toContain()は配列の内容を確認します。toMatch()は文字列に対して正規表現を照合します。toThrow()は、コードがエラーをスローすることを確認します(通常は不正な入力をテストするために)。
このテストスイートでは、関数をインポートし、いくつかの引数と結果を渡す必要があります。通常、複数のシナリオをカバーするために、単一のテスト内に複数の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つの主要なことを行います:
- 配置、データがユニットのために準備される場所
- アクションは単位が呼び出され、整列されたデータが渡され、出力が記録される場所です。
- アサートは、すべてのマッチャーを実行して、すべてが意図した通りに機能していることを確認する場所です。
Reactでのテスト
数学がうまく機能することを確認するのは素晴らしいことですが、おそらくそれほど単純な目的でJestを使用するつもりはないでしょう。あなたはおそらく、Reactのようなフレームワークを使って構築されたJavaScriptアプリケーションのテストを自動化するためにJestを使いたいのです。手動でのレビューを伴う完全なUIテストを行うべきですが、JestはReactコンポーネントを孤立してテストするためにも使えます。結局のところ、ReactコンポーネントはJSX出力を返す関数に過ぎないのです---これを照合してテストすることができます。
始めるには、create-react-appを使って新しいReactプロジェクトをセットアップしてください。
npx create-react-app jest-reactCreate 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を開いてください。これは、デフォルトのReactロゴのテストを含むようにCreate React Appによって既に作成されています。その代わり、デフォルトのテストを以下のように修正してください:
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に無効なボタンが含まれているかどうかをテストします。これは状態をテストできないというわけではありませんが、それが必要かどうかを検討するべきです。

