Was ist ein guter Weg, um zu überschreiben DateTime.Jetzt während der Tests?
-
09-06-2019 - |
Frage
Ich habe einige C# - code, der setzt auf das heutige Datum, um korrekt zu berechnen, die Dinge in der Zukunft.Wenn ich das heutige Datum in der Prüfung, ich muss wiederholen Sie die Berechnung in der Prüfung, das fühlt sich nicht richtig an.Was ist der beste Weg, um das Datum auf einen bekannten Wert im test, so dass ich testen kann, dass das Ergebnis einen bekannten Wert?
Lösung
Meine Vorliebe zu haben Klassen mit der Zeit tatsächlich verlassen sich auf eine Schnittstelle, wie
interface IClock
{
DateTime Now { get; }
}
Mit einem konkreten Umsetzung
class SystemClock: IClock
{
DateTime Now { get { return DateTime.Now; } }
}
Dann, wenn Sie möchten, können Sie jede andere Art von Uhr, die Sie zum testen, wie
class StaticClock: IClock
{
DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}
Es kann ein gewisser Aufwand in der Versorgung der Uhr für die-Klasse, die sich auf Sie, aber Sie kann behandelt werden, indem eine beliebige Anzahl von dependency injection-Lösungen (mit einer Inversion of Control container, plain old Konstruktor/setter-Injektion, oder sogar ein Statische Gateway-Muster).
Andere Mechanismen liefern ein Objekt oder eine Methode, die bietet gewünschten Zeiten auch funktionieren, aber ich denke, der Schlüssel ist, zu vermeiden zurücksetzen der system-Uhr, wie nur vorstellen Schmerz auf anderen Ebenen.
Auch mit DateTime.Now
und wie es in Ihre Berechnungen nicht nur nicht im Recht fühlen - es raubt Ihnen die Fähigkeit zu testen, bestimmten Zeiten, zum Beispiel, wenn Sie entdecken, dass ein Fehler, der passiert nur in der Nähe einer Mitternacht Grenze, oder dienstags.Verwenden Sie die aktuelle Zeit können Sie nicht testen Sie diese Szenarien.Oder zumindest nicht, Wann immer Sie wollen.
Andere Tipps
Ayende Rahien verwendet eine statische Methode ist ziemlich einfach...
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
Ich denke, das erstellen einer separaten Uhr Klasse für etwas einfaches wie das immer das aktuelle Datum ist ein bisschen übertrieben.
Übergeben Sie das heutige Datum als parameter, so können Sie geben Sie ein anderes Datum im test.Dies hat den zusätzlichen Vorteil, dass Sie Ihren code flexibler.
Mithilfe von Microsoft Fälschungen zu erstellen, die ein shim ist ein wirklich einfacher Weg, dies zu tun.Angenommen, ich hatte die folgende Klasse:
public class MyClass
{
public string WhatsTheTime()
{
return DateTime.Now.ToString();
}
}
In Visual Studio 2012 können Sie einen Fälschungen Montage zu Ihre test-Projekt mit der rechten Maustaste auf die assembly, die Sie erstellen möchten Fakes/Beilagen und wählen Sie "Add Fakes-Assembly"
Schließlich, Hier ist das, was der test-Klasse Aussehen würde:
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);
}
}
}
}
Der Schlüssel zu einer erfolgreichen unit-Tests ist Entkopplung.Sie haben zu trennen interessant code von seinen externen Abhängigkeiten, so dass es kann isoliert getestet werden.(Zum Glück, Test-Driven Development, produziert entkoppelt code.)
In diesem Fall, Ihre externe ist die aktuelle DateTime.
Mein Rat hier ist, extrahieren Sie die Logik, das sich mit der DateTime-um eine neue Methode oder Klasse oder was auch immer Sinn macht, in Ihrem Fall, und passieren die DateTime in.Nun, Ihre unit-test übergeben kann eine beliebige DateTime in, zu erzeugen vorhersagbare Ergebnisse.
Ein weiteres mit Microsoft Moles (Isolierung Rahmen für .NET).
MDateTime.NowGet = () => new DateTime(2000, 1, 1);
Muttermale können zu ersetzen .NET Methode mit einem Delegierten.Moles unterstützt statische oder nicht-virtuelle Methoden.Maulwürfe stützt sich auf die profiler von Pex.
Ich würde vorschlagen, mit IDisposable-Muster:
[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);
}
}
Im detail hier beschrieben:http://www.lesnikowski.com/blog/index.php/testing-datetime-now/
Einfache Antwort:Graben-System.DateTime :) verwenden Sie Stattdessen NodaTime und es ist testing library: NodaTime.Prüfung.
Weiter Lesen:
Sie konnte Spritzen Sie die Klasse (besser:Methode/delegieren) verwenden Sie für DateTime.Now
in der Klasse getestet.Haben DateTime.Now
ein Standardwert, nur setzen es in der Prüfung um eine dummy-Methode, dass gibt eine Konstante Wert.
EDIT: Was Blair Conrad sagte (er hat einige code zu schauen).Außer, ich Neige zu bevorzugen, die Delegierten dafür, wie Sie don ' T clutter up your Klassenhierarchie mit Sachen wie IClock
...
Ich Stand vor dieser situation so oft, dass ich erstellt einfache nuget, welche macht Jetzt Eigenschaft durch das interface.
public interface IDateTimeTools
{
DateTime Now { get; }
}
Die Umsetzung ist natürlich sehr einfach
public class DateTimeTools : IDateTimeTools
{
public DateTime Now => DateTime.Now;
}
So nach dem hinzufügen von nuget zu meinem Projekt, ich kann es benutzen in das unit-tests
Sie können installieren Sie das Modul direkt aus dem GUI-Nuget-Paket-Manager oder mit dem Befehl:
Install-Package -Id DateTimePT -ProjectName Project
Und der code für das Nuget ist hier.
Das Beispiel für die Verwendung mit der Autofac finden hier.
Haben Sie als bedingte Kompilierung zu kontrollieren, was passiert, während der debug - / - Bereitstellung?
z.B.
DateTime date;
#if DEBUG
date = new DateTime(2008, 09, 04);
#else
date = DateTime.Now;
#endif
Gelingt das nicht, werden Sie wollen, setzen die Immobilie, so können Sie es verändern, all dies ist Teil der Herausforderung des Schreibens testbar code, die ist etwas, was ich bin derzeit wrestling-mich :D
Bearbeiten
Ein großer Teil von mir würde Präferenz Blair Ansatz.Dadurch können Sie "hot-plug" Teile des Codes, um Hilfe bei der Prüfung.Alles folgt dem design-Prinzip Kapseln, was variiert test code ist nicht anders zu Produktions-code, es ist nur niemand sieht es nach außen.
Erstellen und Schnittstelle mag wie eine Menge Arbeit für dieses Beispiel aber (das ist, warum ich entschied mich für die bedingte Kompilierung).