はじめに
テストを書きたい、でもよくわからない、テストを書く時間がない…と感じたことがある人は、少なくないと思います。
簡単なテストを少し書くだけで時間を節約しつつ品質を高められるなら、テストを書いてみたくなりませんか。
フロントエンドにおけるテストコードの役割を踏まえ、どのようなテストコードが費用対効果を高めるか紹介します。
何をテストするか
フロントエンドはシステムとユーザーの境界、つまり画面を構築することが主目標です。従ってテストコードも基本的には画面表示を主軸に考えることになります。
いいねボタンの実装を考える
例えば、「いいねボタン」の実装を考えてみます。ボタンを押すと画面内のカウントが1つ増える機能です。(バックエンドとの通信などは一旦無視します。)
実装方法は様々ですが、大体以下のような形になると思います。
let likes = 0;
const incrementCount = (prevCount) => {
const newCount = prevCount + 1;
return newCount;
};
const onClickLike = () => {
const newLikes = incrementCount(likes);
render(newLikes);
}
<button onClick={onClickLike}>いいね! { likes }</button>
ユーザー操作を主軸にテストを考える
フロントエンドのテストコードは、まずユーザー操作ベースで考えてみるのがおすすめです。先の例だと
- ボタンを押す
- 画面上のいいねカウントが1増える
という具合です。
つまり、↓ ではなく
let likes = 0;
const incrementCount = (prevCount) => {
const newCount = prevCount + 1;
return newCount;
};
const onClickLike = () => {
incrementCount(likes);
render();
}
こっち ↓ のテストから考える、ということです。
<button onClick={onClickLike}>いいね! { likes }</button>
実装詳細をテストしすぎない
もしかすると「incrementCount関数」 をテストしたくなるかもしれません。しかしながら、時間効率を考える場合それは後回し(あるいは全く行わない)で十分な場合がほとんどです。なぜなら、
- 「受け取った数字に+1して返却する」関数のテストを行なっても、実際の画面表示(実装のゴール)がどうなるかテストできていない
- 上記のユーザー操作のテストは、incrementCount 関数のテストを内包している
からです。
※ 複雑なロジックを持つ関数はもちろんそれ単体でテストを行うことが望ましい。費用対効果で考えることが重要。
テストコードの修正は誰もやりたくない
実装の詳細をテストしないことによって、テストコードの修正を避けることができます。関数ごとのテストをたくさん書いている場合、実装の変更に合わせてテストコードの修正が発生する場合が多々あります。
一方、ユーザー操作ベースで記述されたテストであれば実装が変わってもテストコードに影響はありません。
複雑性をテストする
全てのコードをテストする必要はありません。必要な部分にのみ、テストを書くと良いでしょう。ロジックが絡まない部分のバグは、TypeScriptや ESLintのような解析ツールによって発見できる場合もあります。
まとめ
フロントエンドのテストコードについて、考え方を紹介しました。
- 画面表示、ユーザー操作ベースで”まず”考える
- 上記が不十分な場合、複雑なロジックには別途テストを用意する
- 上記を守ることで、実装変更に強いテストコードを用意できる
- テストコードの修正や、テスト量を抑え、時間的コストを削減できる