回答:
私の好みは、時間を使用するクラスが実際にインターフェースに依存していることです
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); } }
}
それに依存するクラスにクロックを提供する際にオーバーヘッドが発生する可能性がありますが、これは、任意の数の依存性注入ソリューション(Inversion of Controlコンテナー、プレーンな古いコンストラクター/セッター注入、または静的ゲートウェイパターンを使用することでも処理できます))。
希望する時間を提供するオブジェクトまたはメソッドを配信する他のメカニズムも機能しますが、重要なことは、システムクロックのリセットを回避することです。
また、それを使用DateTime.Now
して計算に含めることは、適切に感じられないだけではありません。たとえば、深夜の境界付近や火曜日にのみ発生するバグを発見した場合など、特定の時間をテストする機能を奪います。現在の時刻を使用しても、これらのシナリオをテストすることはできません。または、少なくともいつでも好きなときに。
UtcNow
に使用し、コードの懸念事項(ビジネスロジック、UIなど)に応じて適切に調整する必要があることを追加したいだけです。タイムゾーン全体でのDateTime操作は地雷原ですが、最初の最初の足は常にUTC時間。
Ayende Rahien は、かなり単純な静的メソッドを使用しています...
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
現在の日付を取得するような単純なもののために別の時計クラスを作成することは少しやり過ぎだと思います。
テストで別の日付を入力できるように、今日の日付をパラメーターとして渡すことができます。これには、コードをより柔軟にするという追加の利点があります。
Microsoft Fakesを使用してシムを作成することは、これを行う非常に簡単な方法です。次のクラスがあるとします。
public class MyClass
{
public string WhatsTheTime()
{
return DateTime.Now.ToString();
}
}
Visual Studio 2012では、Fakes / Shimを作成するアセンブリを右クリックして[Fakesアセンブリの追加]を選択することにより、Fakesアセンブリをテストプロジェクトに追加できます。
最後に、テストクラスは次のようになります。
using System;
using ConsoleApplication11;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DateTimeTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestWhatsTheTime()
{
using(ShimsContext.Create()){
//Arrange
System.Fakes.ShimDateTime.NowGet =
() =>
{ return new DateTime(2010, 1, 1); };
var myClass = new MyClass();
//Act
var timeString = myClass.WhatsTheTime();
//Assert
Assert.AreEqual("1/1/2010 12:00:00 AM",timeString);
}
}
}
}
Microsoft Moles(.NETの分離フレームワーク)を使用するもう1つ。
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を。
参考文献:
私はこの状況に頻繁に直面したため、インターフェイスを介してNowプロパティを公開する単純なnugetを作成しました。
public interface IDateTimeTools
{
DateTime Now { get; }
}
もちろん実装は非常に簡単です
public class DateTimeTools : IDateTimeTools
{
public DateTime Now => DateTime.Now;
}
したがって、プロジェクトにnugetを追加した後、単体テストでそれを使用できます
GUI Nuget Package Managerから直接、または次のコマンドを使用してモジュールをインストールできます。
Install-Package -Id DateTimePT -ProjectName Project
そしてNugetのコードはこちらです。
Autofacでの使用例はここにあります。
条件付きコンパイルを使用して、デバッグ/展開中に発生することを制御することを検討しましたか?
例えば
DateTime date;
#if DEBUG
date = new DateTime(2008, 09, 04);
#else
date = DateTime.Now;
#endif
それができない場合は、プロパティを公開して操作できるようにします。これはすべて、テスト可能なコードを書くという課題の一部です。これは、現在私が取り組んでいることです:D
私の大部分はブレアのアプローチを好みます。これにより、コードの一部を「ホットプラグ」してテストを支援できます。それはすべて、設計原則に従っており、変化するものをカプセル化しますコードテストコードをし、本番コードと同じであり、外部からは誰も見ないだけです。
ただし、この例では、作成とインターフェイスの作成は大変な作業のように思えるかもしれません(そのため、条件付きコンパイルを選択しました)。
DateTime
のをNow
してToday
など