より良い単体テストを作るために意識する4つの変更の種類 〜Googleのソフトウェアエンジニアリング〜
概要
「Googleのソフトウェアエンジニアリング」という本の12章は、ユニットテストがテーマです。この中で出てくる4つの変更の種類という考え方が良いと思ったので、備忘として記事にしました。
変更の種類は4つある
理想のテストとは変化しないテストである。つまり、テスト対象システムの要件が変化しない限り、書かれた後は二度と変更の必要がないテストだ。
この本では、理想のテストをテスト対象システムの要件が変化しない限り、2度変更する必要がないテストだとしています。
できるだけ変化しない堅牢なテストを作るためには、ソフトウェアのおける変更の種類と、その変更に対して「テストはどうなることが期待されるか」を理解しておく必要があります。
ソフトウェアにおける変更の種類は、以下に挙げる4つ存在します。
- 純粋なリファクタリング
- 新機能
- バグ修正
- 挙動の変更
それぞれの種類に応じて、あるべきテスト側の対応は異なります。以下で整理します。
1. 純粋なリファクタリング
- リファクタリングする場合、いかなる理由であってもテストは変化すべきではない
- もしテストの変更が必要な場合は、以下のいずれかが考えられる
- システムの挙動を変更しており、予期せぬ影響を与えてしまっている。純粋なリファクタリングではない
- テストが適切に書かれていなかった(脆いテスト/偽陽性を含むテスト)
2. 新機能
- 新機能のためのテストケースを追加する必要がある
- 既存のテストは一切変更されるべきではない
- もし変更が必要な場合は、リファクタリング同様に、予期せぬ影響を与えているか、テストが不適切かのいずれか
3. バグ修正
- 抜けていたテストケースの追加が必要となる
- バグが存在するということは、あるテストケースが現在のテストスイートから抜けていたことになるため
- 既存のテストは一切変更されるべきではない
4. 挙動の変更
- システムの要件が変化し、既存機能の変更するケース
- 4つの変更の中で唯一既存テストを変更する必要がある
4つの変更とテストの対応についてのまとめ
まとめると、テスト対象システムの要件が変化しない限り、既存テストには変更を加えないのが理想の状態だと言えます。4の挙動の変更以外は、既存のテストには一切変更を加えないのが理想です。リファクタリング、新機能追加、バグ修正をする場合、既存テストに手を加えるのは何かが間違っているということです。
システムの要件が変化しない限り、既存テストには変更を加えないのが理想だと認識する
前述の通り、テスト対象システムの要件が変化しない限り、既存テストには変更を加えないのが理想です。
もちろん、既存のテストが常に理想的な状態であるはずもなく、現実には新機能追加やバグ修正でテストが壊れてしまうこともあるでしょう。現実のコードベースには、新機能追加やリファクタリング時に修正しないといけないテストコードは山ほどあると思います。少なくとも私自身はそのような脆いテストを作ってしまったことがありますし、何度も遭遇したこともあります。
しかし、そうであったとしても、テスト対象システムの要件が変化しない限り、既存テストには変更を加えないのが理想であり、そのような堅牢なテストを作るべきなのだと認識しておく必要があると思います。(テストの質を向上させることに余念のない開発者にとっては、こんなことは当たり前のことかもしれませんが...)
私見ですが、このような脆いテストを作ってしまう開発者(以前の私のような)は、システムの要件が変化しない限り、既存テストには変更を加えるべきではないこと、言い換えると偽陽性を含む脆いテストは避けるべきだということをきちんと理解できていないのではないか?と思います。少なくとも私自身は、テストコードを書き始めた頃、この点の理解が曖昧でした。実際、「何か変更を加えるたびに失敗するテストがあって鬱陶しいな。でも、こういうこともあるのかな?仕方ないから、失敗しないようにちょっと修正するか...」という感じで、脆いテストを見過ごしていました。
システム要件が変更される時以外に変化しない堅牢なテストを構築できれば、開発者は既存機能に予期せぬ影響を与えていないことに自信を持って開発ができます。結果として、規模が大きくなっても、開発スピードを落とさず、ソフトウェアは成長を継続できます。
堅牢なテストスイートを維持するためには、まず何よりもシステムの要件が変化しない限り、既存テストには変更を加えないのが理想なのだと認識することだと思います。
最後に
開発する時、コードレビューをする時、常に「この変更が4種類のどれに当たるのか?」「この変更をする場合、テストの変更はどうあるべきか」という視点を持っておくと、テストの質向上に繋がりそうです。
- リファクタリング、新機能追加、バグ修正をする場合に、既存テストに手を加えていないか?
- 既存テストに手を加える場合は、以下のいずれかであるか
-
- システムの要件が変更になった
- 本来テストが失敗するべきではなかったが、テストが不適切だった(脆いテスト)
-
- もし脆いテストが見つかった場合には、次回以降、リファクタリング/新機能/バグ修正で壊れないテストにするために手を打っているか
ということをチェックリストのように頭に入れておきたいです。
Discussion