はじめに
私は、今までテスト駆動開発(TDD)をしらずにソフトウェア開発をしていました。
そして下記のような苦労がありました。
- バグを直そうとしたら、ほかのバグを埋め込んでしまった。
- どこにバグの原因があるかわからず、デバッグに長い時間をかけていた。
- コードをきれいにしたくても既存の振る舞いが変わってないか手動テストするのが大変だった。
TDDを導入することで、下記のメリットを享受できます。
- 安全なリファクタリングができるので、きれいで動くコードをかける。
- 自動テスト可能なコードをかける。
本記事は、私がTDDを実践して、これらのメリットを実感できた記録です。
またテスト対象の課題としてFizz Buzzを選びました。
本記事で紹介する完全なコードは、 GitHub を参照してください。
開発環境
本記事で解説するコードは、下記の環境で開発しました。
- OS
- Windows 10
- テスティングフレームワーク
- NUnit 3.10.1
- IDE
- Visual Studio 2019
- CI
- Circle CI
- VCS
- Git, GitHub
テストファーストでFizz Buzzを実装していく
まずは、テストを書いてテストが失敗することを確認してから実装を書いていきます。
Fizz Buzzのような簡単なプログラムでもストイックに一つ一つテストケースを追加していきました。
[Test]
public void _Fizzが取得できる()
{
var i = FizzBuzz.GetNum();
Assert.That(i.ElementAt(2), Is.EqualTo("Fizz"));
}
[Test]
public void _Buzzが取得できる()
{
var i = FizzBuzz.GetNum();
Assert.That(i.ElementAt(4), Is.EqualTo("Buzz"));
}
[Test]
public void _FizzBuzzが取得できる()
{
var i = FizzBuzz.GetNum();
Assert.That(i.ElementAt(14), Is.EqualTo("FizzBuzz"));
}
リファクタリング中にバグを発見
黄金の回転にしたがいテストがパスしてからリファクタリングをしようとしました。
リファクタリング前
まず下記のコードは、テストをパスしています。
※ リファクタリングに関係のない部分は省略しています。
var i = 0;
while(i++<100)
{
// Fizz Buzzの実装
}
リファクタリング後(失敗)
while
より for
のほうがわかりやすいと思い下記のように書き換えました。
for(var i=0;i<100;i++) // 1から列挙しないといけないのに、0から列挙してしまってる。
{
// Fizz Buzzの実装
}
簡単な書き換えだと思ったので振る舞いは変わらないと思いましたが、
テストを実行すると、テストが失敗したので変更したコードにバグがあることに即座に気づけました。
リファクタリング後(成功)
for(var i=1;i<=100;i++)
{
// Fizz Buzzの実装
}
再度、テストを実行してバグが修正されたことを確認できました。
CIでのビルド
ローカルでテストを実行した結果は、オールグリーンでしたが、GitHubにプッシュしたところCIでのビルドが失敗してしまいました。
原因を調べたところ、メソッド名で、ひらがなや漢字を使っているにも関わらず、UTF-8
ではなく Shift-JIS
で保存されていることが原因でした。
このようにローカルの環境だけでは、わからない問題も、CIで検知できました。
実行可能形式にする
テストコードから、Fizz Buzzの仕様を満たしていることを確認できたので、実行可能なコードを追加して完成です。
static void Main(string[] args)
{
foreach(var i in FizzBuzz.GetNum())
{
Console.WriteLine(i);
}
}
もし、テストファーストをせず最初から実行可能にしていたら副作用によりテストできないコードになっていたことでしょう。
まとめ
私は、自分が思っているほど完璧ではなくミスをしてしまうということを改めて実感しました。
Fizz Buzzという単純な課題においても、TDDとCIの有用性を示せたと思います。