🌊
フロントエンドの単体テストについて
背景
単体テストや結合テストについて、システム開発に従事する人であれば、馴染みがあると思う。単体テストは、単一ユニットから結果を受け取ることに対するテストであり、結合テストは複数ユニットから結果を受け取ることに対するテストである。
一例を挙げると、入力フォームでinputをした際、フロントエンドからバックエンドAPI呼び出し、API呼び出しからDB保存といったそれぞれのテストが単体テストであり、それらを纏めたものが結合テストである。
ユニットテストにもバックエンドとフロントエンドのに種類があるが、フロントエンドの単体テストについて、理解していなかった。バックエンドは単純なAPIの話だからイメージ湧きやすいが、フロントエンドはいまいち掴めず。。というところだった。
フロントエンドの単体テストとは
単体テストは、まずテストの種類を分けることが重要である。簡単なタスク管理アプリのテストを考えてみよう。
- アプリケーションが実行されると、画面にメインUIを表示する
- APIサーバーに「ToDoリスト」を要請し、応答データをReduxストアに格納する
- 保存されたストアの値に基づいてToDoリストをUIで表示する
- ユーザーが入力ボックスをクリックし、「昼寝」と入力してEnterキーを入力する
- APIサーバーに「ToDo追加」を「昼寝」というデータと一緒に要請する
- リクエストが成功すると、Reduxストアのリストに「昼寝」を追加する
- 保存されたストアの値に応じて、UIを更新する
これらのステップは、「アプリケーションを視覚的に表示」する作業と「外部入力により現在の状態を変更」する作業に分けられる。前者は、CSSによる定義で動的制御されることがあるため、検証することが難しい。
テスト一例
下記コードは、単純な+/-のカウンターアプリである。
import React, { useState } from "react";
const Counter = () => {
const [counter, setCounter] = useState(0);
const incrementCounter = () => {
setCounter((prevCounter) => prevCounter + 1);
};
const decrementCounter = () => {
setCounter((prevCounter) => prevCounter - 1);
};
return (
<>
<button data-testid="increment" onClick={incrementCounter}>
+
</button>
<p data-testid="counter">{counter}</p>
<button disabled data-testid="decrement" onClick={decrementCounter}>
-
</button>
</>
);
};
export default Counter;
そして、counter.test.jsファイルには、下記の通りテストコードを書く
- まずCounterファイルをインポートする
- 次に、数値が記載されるcounterを取得する
- クリックすると数値が増えるincrementについて、incrementBtnとして取得する
- 最後に、incrementBtnイベントを発火させて、実際に数値が1となったかチェックする
import { render, fireEvent, screen } from "@testing-library/react";
import Counter from "../components/Counter";
//test block
test("increments counter", () => {
// render the component on virtual dom
render(<Counter />);
//select the elements you want to interact with
const counter = screen.getByTestId("counter");
const incrementBtn = screen.getByTestId("increment");
//interact with those elements
fireEvent.click(incrementBtn);
//assert the expected result
expect(counter).toHaveTextContent("1");
});
下記リンクにて、テストコードを確認可能
引用
Discussion