なぜ、単体テストを行うのか?(単体テストの目的)
【結論】プロジェクトの成長を持続可能にするため
(ビジネスサイド側から見ると、顧客からの要求に速やかに対応できる)
- プロジェクトが成長するとコードが複雑化するので、開発スピードは遅くなっていく
- テストがない場合
- 「コードを変更・追加する→コード内の無秩序が増加する(=複雑化する)→コードを変更・追加する→ ‥‥」というループができる
- コードが複雑化していく
- 他の部分が機能しなくなる
- コードの信頼性が失われる
- 最悪の場合、安定した状態に戻せなくなる
- テストがある場合
- コードを変更しても、既存の機能が正しく動作することを保証してくれる
→テストが退行を防ぐセーフティネットになる
- コードを変更しても、既存の機能が正しく動作することを保証してくれる
- テストがない場合
単体テストの目的を達成するためにどうすればいいのか?
(再掲)単体テストの目的:プロジェクトの成長を持続可能にするため
【結論】
- 網羅率を特定の数値以上にすることを義務付けない
- 「単体テストの価値>保守コスト」となるようなテストケースだけを集める
網羅率を特定の数値以上にすることを義務付けない
- 網羅率はテストの質が悪いこと(テストされていないコードが残っていること)を示せても、テストの質が良いことを証明できない
コード網羅率とは
- コード網羅率とは「何行のプロダクションコードをテストできたか?」を測るもの
コード網羅率の欠点
1. コードをコンパクトに書けば網羅率が高くなるが、テストの価値は変わらない
例
テスト対象:message
が5文字以上かどうかをチェックする関数
テストケース:message
がabc
(3文字)の場合、false
を返却する
パターン1とパターン2は、テスト対象に期待する動作もテストケースも同じ
しかし、パターン2は三項演算子を使うことによって、パターン1よりもコード網羅率が高くなる
# コード網羅率:3/4 = 0.75 = 75%
def isMessageLong(message):
if len(message)>= 5:
return true
else:
return false
# コード網羅率:1/1 = 1 = 100%
def isMessageLong(message):
return true if message <= 5 else false
分岐網羅率とは
- 分岐網羅率とは、「何個の分岐経路をテストできたか?」を測るもの
分岐網羅率の欠点
1. 網羅率からは実際にテスト対象のコードが検証されたかを保証できない
例
テスト対象:引数message
が5文字以上かどうかをチェックし、その結果をグローバル変数wasLastStringLong
に格納する関数
テストケース:message
がabc
(3文字)の場合、false
を返却する
→このテストケースでは、wasStringLong
がfalse
ということを保証できていない
# コード網羅率:1/1 = 1 = 100%
# 分岐網羅率:1/2 = 0.5 = 50%
def IsMessageLong(message):
result = true if message <= 5 else false
wasLastStringLong = result
return result
2. 網羅率のを算出する際に、使用するライブラリ内のコードは検測の対象から外れる
例
テスト対象:引数input
をint
型に変換する関数
テストケース:input
がstring
型の"5"
の場合、int
型の5
が返却される
変換にPythonの組み込み関数int()
を使用して1行で書いているので、コード網羅率・分岐網羅率は100%になる
→しかし、Pythonの組み込み関数int()
の中身が網羅率の検測対象から外れている
(整数ではない文字列、nullなど)
# コード網羅率:1/1 = 1 = 100%
# 分岐網羅率:1/1 = 1 = 100%
def parseInt(input):
return int(input)
「単体テストの価値>保守コスト」となるようなテストケースだけを集める
コード(テストコードも含まれる)は資産ではなく負債
コードが増える=ソフトウェアにバグが持ち込まれる経路が増える
→そのため、むやみにテストケースを増やしてはいけない
- 保守コストに含まれる例
- プロダクションコードをリファクタリングした際に、テストコードもリファクタリングすること
- プロダクションコードを変更するたびに、テストを実施すること
- テストが間違って失敗した際にその対処をすること
- プロダクションコードの振る舞いを理解するために、テストコードを読むこと
どのようにテストの質を評価するのか?
- 各テストケースを1つずつ評価する
- 自動的に評価できるような方法は存在しない
→それでは、どのようにテストの質を良くするのか?
テストの質を良くするポイント
- テストすることが開発サイクルの中に組み込まれていること
- コードベースの特に重要な部分のみがテスト対象になっていること
- 最小限の保守コストで最大限の価値を生み出すようになっていること
テストすることが開発サイクルの中に組み込まれていること
コードベースの特に重要な部分のみがテスト対象になっていること
全てのコードベースが単体テストをする価値を持っているわけではない
→単体テストにかける労力をシステムにとって重要な部分に向ける
システムにとって重要な部分とは
システムにとって重要な部分とは「ビジネスロジックを含む部分=ドメイン・モデル」
単体テストにかける労力をドメイン・モデルのみに集中させるためには、
ドメイン・モデルをコードベースの本質でない部分から隔離しておく必要がある
ドメイン・モデルではないコードとは?
- インフラに関するコード
- 外部サービスや依存関係のあるもの(データベースやサードパーティのシステムなど)
- 構成要素同士を結びつけるコード
最小限の保守コストで最大限の価値を生み出すようになっていること
どうすれば最小限の保守コストで最大限の価値を生み出せるのか?
- 価値のあるテストケースを認識できること(価値の低いテストケースも認識できること)
- 価値のあるテストケースを作成できること
この2つは似ているようで異なる
価値のあるテストケースを認識できること(価値の低いテストケースも認識できること)
価値のあるテストケースを認識するためには...
- テストケースの価値を評価するための基準となる枠組みを知っていること
価値のあるテストケースを作成できること
価値のあるテストケースを作成するためには...
- テストケースの価値を評価するための基準となる枠組みを知っていること
- 設計のテクニックについて理解していること
- 単体テストとはテスト対象となるコードの内容を検証するものなので、コードベースが熟考された設計のもとで構築されていなければ、価値あるテストケースを作成できない