はじめに
プログラムの外部に依存している部分は、ユニットテストが書きにくいものです。ファイルやデータベースへのアクセスなどは、まだなんとかなります。面倒ではありますが、ユニットテストから制御してやればよいのです。そうできない場合はどうしたら良いでしょう? 基本戦略は、「制御できないモノは切り離せ!」です。
今回は、PC内蔵のシステムクロックに依存するコードを例にして、その戦略を考えてみます。
対象読者
- TDDに興味をお持ちの.NET Frameworkの開発者。
必要な環境
サンプルコードを試してみるには、C# 2010(Expressで可)とNUnit 2.6が必要です。本稿執筆時点では、下記から入手できます。
- C# 2010 Express: Microsoft Visual Studio Express
- NUnit 2.6: NUnit V2 2.6.0
- NUnitのインストール手順: NUnit 2.5 の導入 Step by Step(筆者サイト、旧バージョンでの説明ですが基本的に同じです)
※ Visaul Studio 2012 RC/RTMでも構いません。その場合は、Visual StudioのIDEとNUnitを統合できます(『Visual Studio 11 betaの単体テスト機能を使ってみよう!』を参照)。
ユニットテストがうまく書けないコード
返値 |
string型 現在時刻を "HH時 mm分 ss秒" というフォーマットで返す(24時制)。 |
---|
製品コードは、あっさり書けます。
public static string フォーマット済み現在時刻 { get { return DateTimeOffset.Now.ToString("hh時 mm分 ss秒"); } }
このプロパティをテストするコードは、どのように書けば良いでしょう? 例えば、こんなふうでしょうか…?
[TestCase] public void Getフォーマット済み現在時刻Test_これではダメ() { var expected = DateTimeOffset.Now.ToString("hh時 mm分 ss秒"); var result = Class1.フォーマット済み現在時刻; Assert.AreEqual(expected, result); }
これではテストになっていませんし、ごくわずかですがテストが想定外の失敗をする可能性もあります。
テストの期待値expected
を実装と同じコードで作っていますが、これでは同語反復です。テストの意味がありません。実際、この実装にはバグがあるのですが、発見できません。また、このテストはほとんどの場合でGREENになりますが、expected
の算出とresult
の取得の間に運悪く秒が変わってしまうとREDになってしまいます。
緊急避難: Fakesのシムを使う
Visual Studio 2012の上位版に正式搭載されるFakesのシムを使うと、製品コードはそのままで、DateTimeOffset.Now
から返される値を任意に制御することができます。ただし、テストの実行時間が桁違いに長くなりますので、緊急避難的な対応だと考えてください。詳しくは『Visual Studio 11 betaの単体テスト機能を使ってみよう!』をご覧ください。