単体テストの最小単位
単体テストでは、関数ではなく、機能を最小単位としてテストすべきです。
関数を単位としてテストする問題点
伝統的な単体テストでは、関数を単位としてテストします。
-
私的なメソッドとエクスポートされていない関数
エクスポートされていない場合、テストが困難になります。
エクスポートすると、モジュールのカプセル化に影響します。
結果として、これらの私的なメソッドはテストされなくなることが多いです。
-
関数ごとにテストを行う場合、後にロジックの構造を調整する必要が生じた場合、テストの構造も変更する必要があります。
これにより、機能のメンテナンスに加えて、テストのメンテナンスも必要になります。
機能を単位として
単位レベルの機能テスト は Test Driven Development: By Example に由来します。
正しいアプローチは、機能を単位として単体テストを行うことです。
各関数に対して単体テストを書くのではなく、一つの機能を単位としてテストを書きます。
システムは様々な機能で構成されており、機能を単位としてテストすることで、具体的な機能実装のロジック構造を調整しても、テストを変更する必要がなくなります。
これにより、私的なメソッドに対するテストも不要になります。
機能を単位とした単体テストはより丈夫です。
単体テストを作成する四つのステップ
Steps | ||
---|---|---|
1. テスト用データを準備 | given | Arrange(準備) |
2. テスト対象の機能/関数を呼び出す | when | Act(実行) |
3. 機能の出力を検証 | then | Assert(検証) |
4. ティアダウン |
Arrange-Act-Assert (AAA) パターンに従う:準備(Arrange)、実行(Act)、アサート(Assert)。
第一歩と第四歩は必ずしも必要ではありませんが、第二歩と第三歩は必要です。
例えば、sayHi
をテストする時に、データの準備は必要ありません。
function sayHi() {
console.log("Hi!");
}
テスト中にはグローバルなデータやキャッシュを扱うことがありますが、これらは後で元に戻す必要があります。
これがティアダウンの役割です。
しかし、そのようなデータに関わらない場合は、ティアダウンは必要ありません。
最初の単体テストを作成する
以下の単体テストを行ごとにコメントで解説します。
import { test, expect } from "vitest";
import { useTodoStore, reset } from "../todo.ts";
// テストケースは説明的な英語で記述し、テストの目的を明確になるようにします。
test("add todo", () => {
// 1. テスト用データを準備
const todoStore = useTodoStore();
const title = "eat";
// 2. テスト対象の機能/関数を呼び出す
useTodoStore.addTodo(title);
// 3. 機能の出力を検証
expect(todoStore.todos[0].title).toBe(title);
// 4. ティアダウン
reset();
});