Ich habe einige Komponententests, bei denen erwartet wird, dass sich die 'aktuelle Zeit' von DateTime unterscheidet. Jetzt möchte ich die Zeit des Computers natürlich nicht ändern.
Was ist die beste Strategie, um dies zu erreichen?
c#
unit-testing
datetime
mstest
systemtime
Pedro
quelle
quelle
DateTime
. Dies wäre diejenige, die Sie testen, die vorhandene Methode würde einfach die Überladung mit aufrufennow
.Antworten:
Die beste Strategie besteht darin, die aktuelle Zeit in eine Abstraktion zu verpacken und diese Abstraktion dem Verbraucher zuzuführen .
Alternativ können Sie eine Zeitabstraktion auch als Umgebungskontext definieren :
Auf diese Weise können Sie es wie folgt konsumieren:
In einem Komponententest können Sie durch
TimeProvider.Current
ein Test Double / Mock-Objekt ersetzen . Beispiel mit Moq:Denken Sie jedoch beim Testen von Einheiten mit statischem Zustand immer daran, Ihr Gerät durch einen Anruf abzureißen
TimeProvider.ResetToDefault()
.quelle
DateTime.UtcNow
ähnlichen durchsucht , aber Codeüberprüfungen sind trotzdem eine gute Idee.Das sind alles gute Antworten, das habe ich bei einem anderen Projekt gemacht:
Verwendung:
Holen Sie sich das heutige REAL Date Time
Anstatt DateTime.Now zu verwenden, müssen
SystemTime.Now()
Sie ... Es ist keine schwierige Änderung, aber diese Lösung ist möglicherweise nicht für alle Projekte ideal.Zeitreisen (Lass uns 5 Jahre in der Zukunft gehen)
Holen Sie sich unsere Fälschung "heute" (wird 5 Jahre von "heute" sein)
Setzen Sie das Datum zurück
quelle
Maulwürfe:
Haftungsausschluss - Ich arbeite an Maulwürfen
quelle
Sie haben einige Möglichkeiten, dies zu tun:
Verwenden Sie das Mocking-Framework und einen DateTimeService (Implementieren Sie eine kleine Wrapper-Klasse und fügen Sie sie in den Produktionscode ein). Die Wrapper-Implementierung greift auf DateTime zu und in den Tests können Sie die Wrapper-Klasse verspotten.
Verwenden Sie Typemock Isolator , es kann DateTime.Now fälschen und erfordert nicht, dass Sie den zu testenden Code ändern.
Verwenden Sie Maulwürfe , es kann auch DateTime.Now fälschen und erfordert keine Änderung des Produktionscodes.
Einige Beispiele:
Wrapper-Klasse mit Moq:
Fälschen von DateTime direkt mit Isolator:
Haftungsausschluss - Ich arbeite bei Typemock
quelle
Fügen Sie eine gefälschte Baugruppe für das System hinzu (klicken Sie mit der rechten Maustaste auf Systemreferenz => Gefälschte Baugruppe hinzufügen).
Und schreiben Sie in Ihre Testmethode:
quelle
In Bezug auf die Antwort von @crabcrusherclamcollector gibt es ein Problem bei der Verwendung dieses Ansatzes in EF-Abfragen (System.NotSupportedException: Der LINQ-Ausdrucksknotentyp 'Invoke' wird in LINQ to Entities nicht unterstützt). Ich habe die Implementierung dahingehend geändert:
quelle
Um einen Code zu testen, der von der abhängt
System.DateTime
,system.dll
muss der verspottet werden.Es gibt zwei Rahmen, von denen ich weiß, dass sie dies tun. Microsoft Fälschungen und Kittel .
Microsoft-Fälschungen erfordern das Ultimatum von Visual Studio 2012 und funktionieren direkt aus dem Compton heraus.
Smocks ist Open Source und sehr einfach zu bedienen. Es kann mit NuGet heruntergeladen werden.
Das Folgende zeigt ein Modell von
System.DateTime
:quelle
Ein Thread sicher
SystemClock
mitThreadLocal<T>
funktioniert funktioniert gut für mich.ThreadLocal<T>
ist in .Net Framework v4.0 und höher verfügbar.Anwendungsbeispiel:
quelle
Now
Instanz überschreiben .AsyncLocal
nichtThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
Ich bin auf dasselbe Problem gestoßen, habe aber ein Forschungsprojekt von Microsoft gefunden, das dieses Problem löst.
http://research.microsoft.com/en-us/projects/moles/
Moles ist ein leichtes Framework für Teststubs und Umwege in .NET, das auf Delegaten basiert. Maulwürfe können verwendet werden, um jede .NET-Methode zu umgehen, einschließlich nicht virtueller / statischer Methoden in versiegelten Typen
Der Beispielcode wurde gegenüber dem Original geändert.
Ich hatte getan, was andere vorgeschlagen hatten, und die DateTime in einen Anbieter abstrahiert. Es fühlte sich einfach falsch an und ich hatte das Gefühl, dass es zu viel war, nur um es zu testen. Ich werde dies heute Abend in mein persönliches Projekt umsetzen.
quelle
Ein besonderer Hinweis zum Verspotten
DateTime.Now
mit TypeMock ...Der Wert von
DateTime.Now
muss in eine Variable eingefügt werden, damit dies richtig verspottet wird. Beispielsweise:Das funktioniert nicht:
Dies bedeutet jedoch:
quelle
Scheinobjekte.
Eine nachgebildete DateTime, die ein Jetzt zurückgibt, das für Ihren Test geeignet ist.
quelle
Ich bin überrascht, dass niemand einen der naheliegendsten Wege vorgeschlagen hat:
Dann können Sie diese Methode in Ihrem Testdouble einfach überschreiben.
TimeProvider
In einigen Fällen mag ich es auch, eine Klasse zu injizieren , aber für andere ist dies mehr als genug. Ich würde dieTimeProvider
Version wahrscheinlich bevorzugen, wenn Sie dies in mehreren Klassen wiederverwenden müssen.BEARBEITEN: Für alle Interessierten wird dies als Hinzufügen einer "Naht" zu Ihrer Klasse bezeichnet. Dies ist ein Punkt, an dem Sie sich an das Verhalten der Klasse anschließen können, um sie (zu Testzwecken oder auf andere Weise) zu ändern, ohne den Code in der Klasse tatsächlich ändern zu müssen.
quelle
Eine gute Vorgehensweise ist, wenn DateTimeProvider IDisposable implementiert.
Als nächstes können Sie Ihre gefälschte DateTime für Unit-Tests injizieren
Siehe Beispiel Beispiel für DateTimeProvider
quelle
Eine saubere Möglichkeit, dies zu tun, besteht darin, VirtualTime zu injizieren. Damit können Sie die Zeit steuern. Installieren Sie zuerst VirtualTime
So können Sie beispielsweise die Zeit bei allen Aufrufen von DateTime.Now oder UtcNow um das Fünffache verkürzen
Um die Zeit langsamer zu machen, z. B. fünfmal langsamer
Um die Zeit zum Stillstand zu bringen, tun Sie dies
Eine zeitliche Rückwärtsbewegung ist noch nicht getestet
Hier ist ein Beispieltest:
Weitere Tests finden Sie hier:
https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs
Die DateTime.Now.ToVirtualTime-Erweiterung bietet Ihnen eine Instanz von ITime, die Sie an eine Methode / Klasse übergeben, die von ITime abhängt. Einige DateTime.Now.ToVirtualTime werden im DI-Container Ihrer Wahl eingerichtet
Hier ist ein weiteres Beispiel für das Einspritzen in einen Klassenkonfustor
quelle
Wir haben ein statisches SystemTime-Objekt verwendet, sind jedoch auf Probleme beim Ausführen paralleler Komponententests gestoßen. Ich habe versucht, die Lösung von Henk van Boeijen zu verwenden, hatte jedoch Probleme mit erzeugten asynchronen Threads und habe AsyncLocal auf ähnliche Weise wie unten verwendet:
Quelle: https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
quelle
Eine alte Frage, aber immer noch gültig.
Mein Ansatz ist es, eine neue Schnittstelle und Klasse zu erstellen, um den
System.DateTime.Now
Anruf zu beendenDiese Schnittstelle kann in jede Klasse eingefügt werden, die das aktuelle Datum und die aktuelle Uhrzeit abrufen muss. In diesem Beispiel habe ich eine Klasse, die dem aktuellen Datum und der aktuellen Uhrzeit eine Zeitspanne hinzufügt (eine Einheit, die auf System.DateTime.Now.Add (TimeSpan) getestet werden kann).
Und Tests können geschrieben werden, um sicherzustellen, dass es richtig funktioniert. Ich verwende NUnit und Moq, aber jedes Testframework reicht aus
Dieses Muster wird für alle Funktionen arbeitet , die von externen Faktoren abhängen, wie
System.Random.Next()
,System.DateTime.Now.UtcNow
,System.Guid.NewGuid()
usw.Siehe https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core für weitere Beispiele oder erhalten die https://www.nuget.org/packages/Stamina.Core nuget Paket.
quelle
Sie können die Klasse, die Sie testen, so ändern, dass eine Klasse verwendet
Func<DateTime>
wird, die über die Konstruktorparameter übergeben wird. Wenn Sie also eine Instanz der Klasse in echtem Code erstellen, können Sie sie() => DateTime.UtcNow
an denFunc<DateTime>
Parameter übergeben und beim Test die Zeit, die Sie benötigen möchte testen.Beispielsweise:
quelle
Ich hatte das gleiche Problem, aber ich dachte, wir sollten die festgelegten datetime-Dinge nicht für dieselbe Klasse verwenden. weil es eines Tages zu Missbrauch führen könnte. also habe ich den anbieter gerne benutzt
Für Tests machte das Testprojekt einen Helfer, der sich mit festgelegten Dingen befasst,
auf Code
und auf Tests
Aber eines muss man sich merken: Manchmal verhalten sich die echte DateTime und die DateTime des Anbieters nicht gleich
Ich nahm an, dass die Ehrerbietung maximal TimeSpan.FromMilliseconds (0.00002) sein würde . Aber meistens ist es noch weniger
Finden Sie die Probe bei MockSamples
quelle
Hier ist meine Antwort auf diese Frage. Ich kombiniere das 'Ambient Context'-Muster mit IDisposable. Sie können also DateTimeProvider.Current in Ihrem normalen Programmcode verwenden und im Test den Bereich mit einer using-Anweisung überschreiben.
Hier erfahren Sie, wie Sie den obigen DateTimeProvider in einem Unit-Test verwenden
quelle
Mit waren
ITimeProvider
wir gezwungen, es in ein spezielles gemeinsames Projekt aufzunehmen, auf das von den anderen Projekten verwiesen werden muss. Dies erschwerte jedoch die Kontrolle von Abhängigkeiten .Wir haben
ITimeProvider
im .NET Framework nach dem gesucht . Wir haben nach dem NuGet-Paket gesucht und eines gefunden , mit dem wir nicht arbeiten könnenDateTimeOffset
.Deshalb haben wir eine eigene Lösung entwickelt, die nur von den Typen der Standardbibliothek abhängt. Wir verwenden eine Instanz von
Func<DateTimeOffset>
.Wie benutzt man
Wie man sich registriert
Autofac
( Für zukünftige Redakteure: Fügen Sie Ihre Fälle hier hinzu ).
So testen Sie Einheiten
quelle
Möglicherweise könnte eine weniger professionelle, aber einfachere Lösung darin bestehen, einen DateTime-Parameter bei der Consumer-Methode zu erstellen. Beispielsweise können Sie anstelle von make-Methoden wie SampleMethod SampleMethod1 mit parameter erstellen. Das Testen von SampleMethod1 ist einfacher
quelle