将来的に正しく計算するために、今日の日付に依存する(C#)コードがいくつかあります。テストで今日の日付を使用すると、テストで計算を繰り返す必要がありますが、これは正しくありません。結果が既知の値であることをテストできるように、テスト内で日付を既知の値に設定するための最良の方法は何ですか?
11 に答える
私の好みは、時間を使用するクラスが実際に次のようなインターフェースに依存するようにすることです。
interface IClock
{
DateTime Now { get; }
}
具体的な実装で
class SystemClock: IClock
{
DateTime Now { get { return DateTime.Now; } }
}
次に、必要に応じて、テスト用に必要な他の種類の時計を提供できます。
class StaticClock: IClock
{
DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}
それに依存するクラスにクロックを提供することにはいくらかのオーバーヘッドがあるかもしれませんが、それは任意の数の依存性注入ソリューション(制御の反転コンテナ、単純な古いコンストラクタ/セッター注入、または静的ゲートウェイパターンを使用)によって処理できます)。
希望の時間を提供するオブジェクトまたはメソッドを提供する他のメカニズムも機能しますが、重要なことは、システムクロックのリセットを回避することです。これは、他のレベルに苦痛をもたらすだけだからです。
また、それを使用DateTime.Now
して計算に含めることは、正しくないと感じるだけではありません。たとえば、深夜の境界付近や火曜日にのみ発生するバグを発見した場合など、特定の時間をテストする機能が失われます。現在の時刻を使用しても、これらのシナリオをテストすることはできません。または、少なくともあなたが望むときはいつでも。
Ayende Rahienは、かなり単純な静的メソッドを使用しています...
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
現在の日付を取得するような単純なもののために別の時計クラスを作成するのは少しやり過ぎだと思います。
今日の日付をパラメーターとして渡すことができるため、テストで別の日付を入力できます。これには、コードをより柔軟にするという追加の利点があります。
単体テストを成功させる鍵は分離です。興味深いコードを外部の依存関係から分離して、分離してテストできるようにする必要があります。(幸いなことに、テスト駆動開発では分離されたコードが生成されます。)
この場合、外部は現在の DateTime です。
ここでの私のアドバイスは、DateTime を処理するロジックを新しいメソッドまたはクラス、またはケースで意味のあるものに抽出し、DateTime を渡すことです。これで、単体テストで任意の DateTime を渡して、予測可能な結果を生成できます。
もう 1 つは、Microsoft Moles ( .NET の分離フレームワーク) を使用したものです。
MDateTime.NowGet = () => new DateTime(2000, 1, 1);
Moles では、任意の .NET メソッドをデリゲートに置き換えることができます。Moles は、静的メソッドまたは非仮想メソッドをサポートしています。Moles は、Pex のプロファイラーに依存しています。
IDisposable パターンを使用することをお勧めします。
[Test]
public void CreateName_AddsCurrentTimeAtEnd()
{
using (Clock.NowIs(new DateTime(2010, 12, 31, 23, 59, 00)))
{
string name = new ReportNameService().CreateName(...);
Assert.AreEqual("name 2010-12-31 23:59:00", name);
}
}
詳細はこちら: http://www.lesnikowski.com/blog/index.php/testing-datetime-now/
簡単な答え: System.DateTime を捨てる :) 代わりに、NodaTimeを使用し、それはテスト ライブラリ: NodaTime.Testingです。
参考文献:
テスト対象のクラスで使用するクラス(より良い:メソッド/デリゲート)を注入できます。DateTime.Now
デフォルトDateTime.Now
値であり、テストでは定数値を返すダミーメソッドにのみ設定します。
編集:ブレアコンラッドが言ったこと(彼は見るべきいくつかのコードを持っています)。ただし、デリゲートはクラス階層を次のようなもので乱雑にしないため、デリゲートを好む傾向がありますIClock
...
条件付きコンパイルを使用して、デバッグ/展開中に何が発生するかを制御することを検討しましたか?
例えば
DateTime date;
#if DEBUG
date = new DateTime(2008, 09, 04);
#else
date = DateTime.Now;
#endif
それができない場合は、プロパティを公開して操作できるようにします。これはすべて、テスト可能なコードを作成するという課題の一部です。これは、私が現在取り組んでいることです:D
編集
私の大部分はブレアのアプローチを好むでしょう。これにより、コードの一部を「ホットプラグ」してテストに役立てることができます。それはすべて、テストコードが変化するものをカプセル化する設計原則に従っており、本番コードと同じであり、外部からは誰も見ません。
ただし、この例では、作成とインターフェイスは大変な作業のように思われるかもしれません(これが、条件付きコンパイルを選択した理由です)。