本ガイドラインの目的、目指すところ
- テストの基本的な書き方、考え方を習得する
- 原因調査時などに手動確認を減らせるテストを書けるようになる
本ガイドラインの目的外のこと: リファクタしやすくなるようにテストファイルを整備する。QAの代替を目指す。 (level2として書くかも)
テストを書く際の流れ
テストは以下の流れに沿って書く。
- テスト分析 (テストで確認したい事項を考え、メソッド名を決める)
- テスト実装 (実際のテストメソッドの中身を実装する)
- テスト実行 (実際にテストしてみる)
特に テスト分析を実施すること。 いきなりテストの実装から始めない。
1. 分析: テストで確認したい事項を考え、メソッド名を決める
テストの確認事項は 「手動でQAするとしたら何を調べるか?」を基準にまず考える。 そして、それを自動化できるようなソフトウェアテストを作る。
確認事項の悪い例:
- 青森県の場合 (→その場合に期待されているアプリの挙動がが分からない)
- method is OK (→同じく期待するアプリの挙動がわからない)
- 返り値が正しいか? (→仕様が変化して「正しさ」の基準が変わったときに追従できない)
- 与えられたひらがなに対応するローマ字の配列を返しているか? (→QA観点として大雑把すぎる)
確認事項の良い例:
- 東北地方の場合送料が1000円か?
- 選択している項目が6件未満だったら保存できないか?
- 人数が0名の場合予約できないか? / 人数が1名以上4名以下の場合予約できるか?
悪い例は手動QAするのが難しい。 例えば「返り値が正しいか?」は、具体的にどうなれば正しいのか分からず、手動QAできない。
また、 悪い例のほうはテストがパスしても得られる情報が少ない。 たとえば「青森県の場合」というテストケースがパスしたという情報にどういう意味があるかは、テスト実装を確認しなければ分からない。
良い例のように、テストが何を確認しているのか、テストがどういう挙動を保証するのかを明確にして、情報量を増やすべき。
参考: テストコードにはテストの意図を込めよう #vstat
1つのテストケースで確認する項目は1つに絞る
1つのテストケースで確認する項目は、原則として1つとする。確認項目を絞ると様々なメリットがある(テスト実装が短くなって読みやすくなる、テストを修正しやすくなる、テストを追加するのが容易となるなど)。
例として、ひらがなをローマ字に変換する関数(transformToRoman)のテスト項目を考える。
悪いテスト項目:
- 与えられたひらがなに対応するローマ字の配列を返しているか?
良いテスト項目:
- うしろに"な行"か"や行"が続く場合、"ん"がnに変換されるか? 続かない場合はnnに変換されるか?
- アルファベットが続く場合"っ"はxtuに変換されるか?
- "っ"は後ろに続くひらがなの子音を含んで変換されるか?
悪い例は観点の粒度が大きく、複数の仕様が包含されている。 良い例では、これを細かい仕様に分割している。
良い例は粒度が細かく、 ローマ字変換の仕様についても明確に読み取ることができる。
(参考: リーダブルコード p.190)
期待値を具体的にする
期待値は「正しいか」や「問題ないか」ではなく、具体的に書く。 「正しいか」「問題ないか」は期待結果が曖昧である。
期待結果があいまいだと、異なる解釈をされる可能性があり、テストコード修正時やテスト失敗時に困る。
- 悪い例:
transformToRoman は正しい返り値を返しているか?
→ 関数に期待している挙動がわからない
- 良い例:
transformToRoman はアルファベットが続く場合に"っ"をxtuに変換するか?
→ 関数に期待する挙動が明確。
またそもそも、テストがpassする条件が揃ったときに結果として「正しい」のである。そのため、 「正しいか」という確認事項は何も言ってないのと同じである。
メソッド名について
基本的にメソッド名は、テスト項目を英訳した文章をつける。たとえば「東北地方の場合送料が1000円か?」をメソッド名にすると「calcPostage returns 1000yen for Tohoku region」となる。
describe('calcPostage()', () => {
it('returns 1000yen for Tohoku region', () => {
});
});
このようにすることで、 メソッド名を読むだけで、コードがどういう仕様を満たせているかすぐに分かる。
2. 実装: 実際のテストメソッドの中身を実装する
テスト実装は、テスト分析の際に決めた確認したい事項を確認できるように書く。
気をつけるポイント:
- カバレッジを上げる際、確認項目にぬけもれがないことを確認する
カバレッジを上げる際、確認項目にぬけもれがないことを確認する
たとえば以下のテストはカバレッジ100%だが重要なアルゴリズムのミスを見落としている。
const sort = arr => {
const acc = [];
for (const x of arr[i]) {
if (acc.length === 0 || acc[acc.length - 1] <= x) {
acc.push(x);
}
}
return acc;
};
test('sort() orders elements from smallest to highest', () => {
const result = sort([0, 1, 3, 2, 4]);
expect(
result.every((val, idx) => idx === 0 || result[idx-1] < val )
).toBe(true);
});
このように、カバレッジが100%でも満たしてほしい仕様を網羅できてないことがある。 そのため、カバレッジを上げる際には気をつける (カバレッジはテストされてない領域を探すためにしか使えない!)
参考: ソフトウェアテストの7原則とは? (テストでは欠陥があることしか示せない)
3. 実行: 実際にテストしてみる
実際にテストをするのも大事だ。
たとえば、ソフトウェアが壊れているときにテストが成功しないようになっているか(false negativeになっていないか)を確認してみる。コードを壊してもテストが通ったら確認事項が漏れている。参考: Make Your Test Fail
また、テストケースの順序を入れ替えたときに破綻しないかも確認する。もしテストケースを入れ替えたときにテストがパスしなくなった場合、テスト後のクリーンアップができていない可能性が高い。テスト後に環境が元通りになるようにしておくこと。
参考