J'ai quelques tests unitaires qui s'attendent à ce que «l'heure actuelle» soit différente de DateTime.Now et je ne veux évidemment pas changer l'heure de l'ordinateur.
Quelle est la meilleure stratégie pour y parvenir?
J'ai quelques tests unitaires qui s'attendent à ce que «l'heure actuelle» soit différente de DateTime.Now et je ne veux évidemment pas changer l'heure de l'ordinateur.
Quelle est la meilleure stratégie pour y parvenir?
DateTime
. Ce serait celui que vous testez, la méthode existante appellerait simplement la surcharge avecnow
.Réponses:
La meilleure stratégie consiste à envelopper l'heure actuelle dans une abstraction et à injecter cette abstraction dans le consommateur .
Vous pouvez également définir une abstraction temporelle en tant que contexte ambiant :
Cela vous permettra de le consommer comme ceci:
Dans un test unitaire, vous pouvez le remplacer
TimeProvider.Current
par un objet Test Double / Mock. Exemple utilisant Moq:Cependant, lors de tests unitaires avec un état statique, n'oubliez pas de démonter votre appareil en appelant
TimeProvider.ResetToDefault()
.la source
DateTime.UtcNow
et similaire, mais les révisions de code sont quand même une bonne idée.Ce sont toutes de bonnes réponses, c'est ce que j'ai fait sur un projet différent:
Usage:
Obtenir l'heure réelle de la date d'aujourd'hui
Au lieu d'utiliser DateTime.Now, vous devez utiliser
SystemTime.Now()
... Ce n'est pas un changement difficile mais cette solution n'est peut-être pas idéale pour tous les projets.Voyage dans le temps (partons 5 ans dans le futur)
Get Our Fake "aujourd'hui" (dans 5 ans à compter d'aujourd'hui)
Réinitialiser la date
la source
Taupes:
Clause de non-responsabilité - Je travaille sur les taupes
la source
Vous avez quelques options pour le faire:
Utilisez un framework mocking et utilisez un DateTimeService (implémentez une petite classe wrapper et injectez-la dans le code de production). L'implémentation du wrapper accédera à DateTime et dans les tests, vous pourrez vous moquer de la classe wrapper.
Utilisez Typemock Isolator , il peut simuler DateTime.Now et ne vous obligera pas à modifier le code testé.
Utilisez Moles , il peut également simuler DateTime.Now et ne nécessitera pas de changement de code de production.
Quelques exemples:
Classe Wrapper utilisant Moq:
Fake DateTime directement en utilisant Isolator:
Clause de non-responsabilité - Je travaille chez Typemock
la source
Ajouter un faux assemblage pour le système (clic droit sur Référence système => Ajouter un faux assemblage).
Et écrivez dans votre méthode de test:
la source
En ce qui concerne la réponse @crabcrusherclamcollector, il y a un problème lors de l'utilisation de cette approche dans les requêtes EF (System.NotSupportedException: le type de nœud d'expression LINQ 'Invoke' n'est pas pris en charge dans LINQ to Entities). J'ai modifié l'implémentation pour cela:
la source
Pour tester un code qui dépend de
System.DateTime
, lesystem.dll
doit être simulé.Il y a deux cadres que je connais qui font cela. Microsoft faux et smocks .
Les faux Microsoft nécessitent un ultimatum de Visual Studio 2012 et fonctionnent directement à partir du compton.
Smocks est un open source et très facile à utiliser. Il peut être téléchargé à l'aide de NuGet.
Ce qui suit montre une simulation de
System.DateTime
:la source
Une
SystemClock
utilisation sans filThreadLocal<T>
fonctionne très bien pour moi.ThreadLocal<T>
est disponible dans le .Net Framework v4.0 et supérieur.Exemple d'utilisation:
la source
Now
instance actuelle .AsyncLocal
plutôt queThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
J'ai rencontré ce même problème, mais j'ai trouvé un projet de recherche de Microsoft qui résout ce problème.
http://research.microsoft.com/en-us/projects/moles/
Moles est un framework léger pour les stubs de test et les détours dans .NET basé sur des délégués. Les taupes peuvent être utilisées pour détourner toute méthode .NET, y compris les méthodes non virtuelles / statiques dans les types scellés
L'exemple de code a été modifié par rapport à l'original.
J'avais fait ce que d'autres suggéraient et résumais le DateTime dans un fournisseur. Je me sentais juste mal et j'avais l'impression que c'était trop juste pour les tests. Je vais mettre cela en œuvre dans mon projet personnel ce soir.
la source
Une note spéciale sur les moqueries
DateTime.Now
avec TypeMock ...La valeur de
DateTime.Now
doit être placée dans une variable pour que cela soit correctement simulé. Par exemple:Cela ne fonctionne pas:
Cependant, cela fait:
la source
Objets simulés.
Un DateTime simulé qui renvoie un Now qui convient à votre test.
la source
Je suis surpris que personne n'ait suggéré l'une des façons les plus évidentes de procéder:
Ensuite, vous pouvez simplement remplacer cette méthode dans votre test double.
J'aime aussi un peu injecter une
TimeProvider
classe dans certains cas, mais pour d'autres, c'est plus que suffisant. Je préférerais probablement laTimeProvider
version si vous devez la réutiliser dans plusieurs classes.EDIT: Pour toute personne intéressée, cela s'appelle l'ajout d'une "couture" à votre classe, un point où vous pouvez vous connecter à son comportement pour le modifier (à des fins de test ou autrement) sans avoir à changer le code de la classe.
la source
La bonne pratique est, lorsque DateTimeProvider implémente IDisposable.
Ensuite, vous pouvez injecter votre faux DateTime pour les tests unitaires
Voir l'exemple Exemple de DateTimeProvider
la source
Une manière propre de faire ceci est d'injecter VirtualTime. Cela vous permet de contrôler le temps. Installez d'abord VirtualTime
Cela permet, par exemple, de rendre le temps qui passe 5 fois plus vite sur tous les appels à DateTime.Now ou UtcNow
Pour ralentir le temps, par exemple 5 fois plus lentement, faites
Pour que le temps s'arrête, faites
Le recul dans le temps n'est pas encore testé
Voici un exemple de test:
Vous pouvez consulter plus de tests ici:
https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs
L'extension DateTime.Now.ToVirtualTime vous donne une instance d'ITime que vous passez à une méthode / classe qui dépend d'ITime. certains DateTime.Now.ToVirtualTime sont configurés dans le conteneur DI de votre choix
Voici un autre exemple d'injection dans un contrôleur de classe
la source
Nous utilisions un objet SystemTime statique, mais nous avons rencontré des problèmes lors de l'exécution de tests unitaires parallèles. J'ai essayé d'utiliser la solution de Henk van Boeijen mais j'ai eu des problèmes sur les threads asynchrones générés, j'ai fini par utiliser AsyncLocal d'une manière similaire à celle ci-dessous:
Tiré de https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
la source
Une vieille question, mais toujours d'actualité.
Mon approche consiste à créer une nouvelle interface et une nouvelle classe pour encapsuler l'
System.DateTime.Now
appelCette interface peut être injectée dans n'importe quelle classe qui a besoin d'obtenir la date et l'heure actuelles. Dans cet exemple, j'ai une classe qui ajoute une période à la date et à l'heure actuelles (une unité testable System.DateTime.Now.Add (TimeSpan))
Et des tests peuvent être écrits pour s'assurer qu'il fonctionne correctement. J'utilise NUnit et Moq mais n'importe quel framework de test fera l'affaire
Ce modèle fonctionne pour toutes les fonctions qui dépendent de facteurs externes, comme
System.Random.Next()
,System.DateTime.Now.UtcNow
,System.Guid.NewGuid()
etc.Voir https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core pour plus d'exemples ou obtenir le package https://www.nuget.org/packages/Stamina.Core nuget.
la source
Vous pouvez changer la classe que vous testez pour utiliser une
Func<DateTime>
qui sera passée à travers ses paramètres de constructeur, donc lorsque vous créez une instance de la classe dans du code réel, vous pouvez passer() => DateTime.UtcNow
auFunc<DateTime>
paramètre, et sur le test, vous pouvez passer le temps que vous souhaite tester.Par exemple:
la source
J'ai eu le même problème, mais je pensais que nous ne devrions pas utiliser les éléments datetime définis sur la même classe. car cela pourrait conduire un jour à une mauvaise utilisation. donc j'ai utilisé le fournisseur comme
Pour les tests, lors du projet de test, vous avez créé un assistant qui s'occupera des choses définies,
sur code
et sur les tests
Mais il faut se rappeler une chose, parfois le DateTime réel et le DateTime du fournisseur ne fonctionnent pas de la même manière
J'ai supposé que la déférence serait maximale TimeSpan.FromMilliseconds (0.00002) . Mais la plupart du temps c'est encore moins
Trouvez l'échantillon sur MockSamples
la source
Voici ma réponse à cette question. Je combine le modèle 'Ambient Context' avec IDisposable. Ainsi, vous pouvez utiliser DateTimeProvider.Current dans votre code de programme normal et dans le test, vous remplacez la portée avec une instruction using.
Voici comment utiliser le DateTimeProvider ci-dessus dans un test unitaire
la source
En utilisant,
ITimeProvider
nous avons été obligés de le prendre dans un projet commun partagé spécial qui doit être référencé à partir du reste des autres projets. Mais cela a compliqué le contrôle des dépendances .Nous avons recherché le
ITimeProvider
dans le framework .NET. Nous avons recherché le package NuGet et en avons trouvé un qui ne fonctionne pas avecDateTimeOffset
.Nous avons donc proposé notre propre solution, qui ne dépend que des types de bibliothèque standard. Nous utilisons une instance de
Func<DateTimeOffset>
.Comment utiliser
Comment s'inscrire
Autofac
( Pour les futurs éditeurs: ajoutez vos cas ici ).
Comment effectuer un test unitaire
la source
Peut-être qu'une solution moins professionnelle mais plus simple pourrait être de créer un paramètre DateTime à la méthode du consommateur.Par exemple, au lieu de faire une méthode comme SampleMethod, créez SampleMethod1 avec le paramètre.
la source