Question

J'ai du code (C#) qui s'appuie sur la date d'aujourd'hui pour calculer correctement les choses dans le futur.Si j'utilise la date du jour dans le test, je dois répéter le calcul dans le test, ce qui ne me semble pas correct.Quelle est la meilleure façon de définir la date sur une valeur connue dans le test afin de pouvoir vérifier que le résultat est une valeur connue ?

Était-ce utile?

La solution

Ma préférence est que les classes qui utilisent du temps s'appuient réellement sur une interface, telle que

interface IClock
{
    DateTime Now { get; } 
}

Avec une mise en œuvre concrète

class SystemClock: IClock
{
     DateTime Now { get { return DateTime.Now; } }
}

Ensuite, si vous le souhaitez, vous pouvez fournir tout autre type d'horloge à des fins de test, comme

class StaticClock: IClock
{
     DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}

Il peut y avoir une certaine surcharge liée à la fourniture de l'horloge à la classe qui en dépend, mais cela pourrait être géré par un certain nombre de solutions d'injection de dépendances (en utilisant un conteneur d'inversion de contrôle, une simple injection de constructeur/setter, ou même un Modèle de passerelle statique).

D'autres mécanismes permettant de fournir un objet ou une méthode fournissant les heures souhaitées fonctionnent également, mais je pense que l'essentiel est d'éviter de réinitialiser l'horloge du système, car cela ne fera qu'introduire des problèmes à d'autres niveaux.

Aussi, en utilisant DateTime.Now et l'inclure dans vos calculs ne semble pas juste - cela vous prive de la possibilité de tester des moments particuliers, par exemple si vous découvrez un bug qui ne se produit que près de minuit ou le mardi.L'utilisation de l'heure actuelle ne vous permettra pas de tester ces scénarios.Ou du moins pas quand vous le souhaitez.

Autres conseils

Ayende Rahien les usages une méthode statique plutôt simple...

public static class SystemTime
{
    public static Func<DateTime> Now = () => DateTime.Now;
}

Je pense que créer une classe d'horloge distincte pour quelque chose de simple comme obtenir la date actuelle est un peu excessif.

Vous pouvez transmettre la date du jour en tant que paramètre afin de pouvoir saisir une date différente dans le test.Cela présente l’avantage supplémentaire de rendre votre code plus flexible.

Utiliser Microsoft Fakes pour créer une cale est un moyen très simple de le faire.Supposons que j'ai le cours suivant :

public class MyClass
{
    public string WhatsTheTime()
    {
        return DateTime.Now.ToString();
    }

}

Dans Visual Studio 2012, vous pouvez ajouter un assemblage Fakes à votre projet de test en cliquant avec le bouton droit sur l'assemblage pour lequel vous souhaitez créer des Fakes/Shims et en sélectionnant « Ajouter un assemblage Fakes ».

Adding Fakes Assembly

Enfin, voici à quoi ressemblerait la classe de test :

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);

        }
    }
}
}

La clé du succès des tests unitaires est découplage.Vous devez séparer votre code intéressant de ses dépendances externes, afin qu'il puisse être testé de manière isolée.(Heureusement, le développement piloté par les tests produit du code découplé.)

Dans ce cas, votre externe est le DateTime actuel.

Mon conseil ici est d'extraire la logique qui traite du DateTime vers une nouvelle méthode ou classe ou tout ce qui a du sens dans votre cas, et de transmettre le DateTime.Désormais, votre test unitaire peut transmettre un DateTime arbitraire pour produire des résultats prévisibles.

Un autre utilisant Microsoft Moles (Cadre d'isolation pour .NET).

MDateTime.NowGet = () => new DateTime(2000, 1, 1);

Les taupes permettent de remplacer toute méthode .NET par un délégué.Les taupes prennent en charge les méthodes statiques ou non virtuelles.Les taupes s'appuient sur le profileur de PEX.

Je suggère d'utiliser le modèle 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);
    } 
}

En détail décrit ici:http://www.lesnikowski.com/blog/index.php/testing-datetime-now/

Réponse simple :abandonnez System.DateTime :) À la place, utilisez NodaTime et c'est la bibliothèque de tests : NodaTime.Testing.

Lectures complémentaires :

Vous pouvez injecter la classe (mieux :méthode/déléguer) vous utilisez pour DateTime.Now dans la classe testée.Avoir DateTime.Now être une valeur par défaut et définissez-la uniquement lors des tests sur une méthode factice qui renvoie une valeur constante.

MODIFIER: Ce que Blair Conrad a dit (il a du code à examiner).Sauf que j'ai tendance à préférer les délégués pour cela, car ils n'encombrent pas votre hiérarchie de classe avec des trucs comme IClock...

J'ai été confronté à cette situation si souvent que j'ai créé un simple nuget qui expose Maintenant propriété via l’interface.

public interface IDateTimeTools
{
    DateTime Now { get; }
}

La mise en œuvre est bien sûr très simple

public class DateTimeTools : IDateTimeTools
{
    public DateTime Now => DateTime.Now;
}

Ainsi, après avoir ajouté nuget à mon projet, je peux l'utiliser dans les tests unitaires

enter image description here

Vous pouvez installer le module directement à partir du GUI Nuget Package Manager ou en utilisant la commande :

Install-Package -Id DateTimePT -ProjectName Project

Et le code du Nuget est ici.

L'exemple d'utilisation avec l'Autofac peut être trouvé ici.

Avez-vous envisagé d'utiliser la compilation conditionnelle pour contrôler ce qui se passe pendant le débogage/le déploiement ?

par exemple.

DateTime date;
#if DEBUG
  date = new DateTime(2008, 09, 04);
#else
  date = DateTime.Now;
#endif

A défaut, vous souhaitez exposer la propriété afin de pouvoir la manipuler, tout cela fait partie du défi de l'écriture. testable code, c'est quelque chose que je lutte moi-même actuellement :D

Modifier

Une grande partie de moi préférerait L'approche de Blair.Cela vous permet de « brancher à chaud » des parties du code pour faciliter les tests.Tout suit le principe de conception résumer ce qui varie le code de test n’est pas différent du code de production, c’est juste que personne ne le voit de l’extérieur.

La création et l'interface peuvent cependant sembler beaucoup de travail pour cet exemple (c'est pourquoi j'ai opté pour la compilation conditionnelle).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top