デバッグ(その2)
新人テスター「あの、b先輩。バグ票です…」
b君「ご苦労さま。あ、1枚だけ? じゃ、内容を140字以内で説明してみて♪」
新人テスター「ええっと…。12を入れると、"Fizz"のはずが、"Buzz"が出てきます」
b君「了解。よく見つけてくれたね、ありがとう」(よしよし、ちゃんと入力・期待値・実際の出力を区別して言えるようになったな!)
新人テスター「あの…、チーフが今日中にと…」
b君「あー、たぶん僕の単純ミスだからすぐ直ると思うよ。そう伝えておいて」
b君は、12のときのテストケースを書いた覚えがあったので、たぶんそのテストケースを書き間違えた単純なミスだろうとアタリを付けています。前述したテストケース漏れでなければ、バグの原因はテストケースの誤りにあるはずです。まず、そのテストケースを見つけます。
[TestCase(12, "Buzz")]
たしかに間違えています。12のときは"Fizz"が返らなくてはいけませんね。テストケースを修正して、バグを再現させます。
[TestCase(12, "Fizz")]
テストケースを修正したら、予想通りのREDになることを必ず確認します。
CsTdd04.FizzBuzzTest.FizzBuzzerTest.SayTest(12,"Fizz"): String lengths are both 4. Strings differ at index 0. Expected: "Fizz" But was: "Buzz" -----------^
報告されたバグを再現するテストケースができました。これを含めてオールGREENにできれば、バグフィックスは完了です。直すのは簡単ですね。では、製品コードを修正します。
public string Say(int n) { // 略 switch (n % 15) { case 3: case 6: case 9: case 12: // ←追加 return "Fizz"; case 5: case 10: //case 12: // ←削除 return "Buzz"; // 略 } }
これでオールGREENになったので、デバッグ完了です。なお、今回の例では、リファクタリングが必要な場面が出てきませんでしたが、実際の開発ではデバッグの前後にリファクタリングをすることもよくあります。
注意点としては、デバッグ中(再現テストを書いてREDになっている間)はリファクタリングしてはいけません。リファクタリングはオールGREENを維持しつつ、実装を改善するものだからです。
まとめ
- 仕様変更も、新仕様を表現するテストケースを書いて、RED ⇒ GREEN ⇒ リファクタリング
- デバッグも、バグを再現するテストケースを書いて、RED ⇒ GREEN ⇒ リファクタリング
- 最小限のテストケースと、テストコードも含めたリファクタリングによって、変更時のテストコードの修正量を減らすことができる
説明に使ったサンプルコードはあまりにも単純なものなので、実際の開発ではこれほど簡単にいかないことも多いでしょう。1つの仕様変更のために修正すべき製品コードのメソッドが数十にも及ぶとか、1か所バグを修正したら何十ものテストケースがREDになるとか、そんなこともあります。それでも、原則は変わりません。まずテストケース(外部設計)を変更し、それから製品コード(内部設計)を修正します。