221 votes

Tests unitaires : DateTime.Now

J’ai quelques tests unitaires qui attend le « temps actuel » à être différent de DateTime.Now et je ne veux pas changer l’heure de l’ordinateur, évidemment. Quelle est la meilleure stratégie pour y parvenir ?

Merci

260voto

Mark Seemann Points 102767

La meilleure stratégie consiste à envelopper l'heure actuelle dans une abstraction et injecter cette abstraction dans la consommation.

Alternativement, vous pouvez également définir un temps de l'abstraction comme un Ambiantes Contexte:

public abstract class TimeProvider
{
    private static TimeProvider current =
        DefaultTimeProvider.Instance;

    public static TimeProvider Current
    {
       get { return TimeProvider.current; }
       set 
       {
           if (value == null)
           {
               throw new ArgumentNullException("value");
           }
           TimeProvider.current = value; 
       }
   }

   public abstract DateTime UtcNow { get; }

   public static void ResetToDefault()
   {    
       TimeProvider.current = DefaultTimeProvider.Instance;
   }            
}

Cela vous permettra de consommer comme ceci:

var now = TimeProvider.Current.UtcNow;

Dans un test unitaire, vous pouvez remplacer TimeProvider.Current avec un Test Double/objet Fantaisie. Exemple d'utilisation Moq:

var timeMock = new Mock<TimeProvider>();
timeMock.SetupGet(tp => tp.UtcNow).Returns(new DateTime(2010, 3, 11));
TimeProvider.Current = timeMock.Object;

Toutefois, lorsque les tests unitaires avec l'état statique, rappelez-vous de toujours le démontage de votre luminaire en appelant TimeProvider.ResetToDefault().

69voto

Ce sont toutes de bonnes réponses, c'est ce que j'ai fait sur un autre projet:

Utilisation:

Obtenez RÉEL d'aujourd'Hui date Heure

var aujourdhui = SystemTime.Maintenant().Date;

Au lieu d'utiliser DateTime.Maintenant, vous devez utiliser SystemTime.Maintenant()... C'est pas dur à changer, mais cette solution pourrait ne pas être idéal pour tous les projets.

Le temps d'un Voyage (Permet de passer 5 ans dans l'avenir)

SystemTime.SetDateTime(d'aujourd'hui.AddYears(5));

Bénéficiez de Nos Faux "aujourd'hui" (de 5 ans à partir de "aujourd'hui")

var fakeToday = SystemTime.Maintenant().Date;

Réinitialiser la date

SystemTime.ResetDateTime();

/// <summary>
/// Used for getting DateTime.Now(), time is changeable for unit testing
/// </summary>
public static class SystemTime
{
    /// <summary> Normally this is a pass-through to DateTime.Now, but it can be overridden with SetDateTime( .. ) for testing or debugging.
    /// </summary>
    public static Func<DateTime> Now = () => DateTime.Now;

    /// <summary> Set time to return when SystemTime.Now() is called.
    /// </summary>
    public static void SetDateTime(DateTime dateTimeNow)
    {
        Now = () =>  dateTimeNow;
    }

    /// <summary> Resets SystemTime.Now() to return DateTime.Now.
    /// </summary>
    public static void ResetDateTime()
    {
        Now = () => DateTime.Now;
    }
}

25voto

Peli Points 2112

Grains de beauté :

Clause de non-responsabilité - je travaille sur les taupes

25voto

Elisha Points 11999

Vous avez quelques options pour le faire:

  1. L'utilisation se moquant de cadre et de l'utilisation d'un DateTimeService (mettre en Œuvre un petit emballage de la classe et de l'injecter à la production de code). Le wrapper de mise en œuvre accès DateTime et dans les tests que vous serez en mesure de se moquer de la classe wrapper.

  2. Utilisation Typemock Isolateur, il peut faux DateTime.Maintenant et ne vous demandera pas de changer le code en cours de test.

  3. Utiliser les Taupes, il peut aussi faux DateTime.Maintenant et ne nécessite pas de changement dans le code de production.

Quelques exemples:

Classe Wrapper en utilisant Moq:

[Test]
public void TestOfDateTime()
{
     var mock = new Mock<IDateTime>();
     mock.Setup(fake => fake.Now)
         .Returns(new DateTime(2000, 1, 1));

     var result = new UnderTest(mock.Object).CalculateSomethingBasedOnDate();
}

public class DateTimeWrapper : IDateTime
{
      public DateTime Now { get { return DateTime.Now; } }
}

Faking DateTime directement à l'aide d'Isolateur:

[Test]
public void TestOfDateTime()
{
     Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));

     var result = new UnderTest().CalculateSomethingBasedOnDate();
}

Avertissement - je travaille à Typemock

1voto

S.Lott Points 207588

Objets factices.

A se moquer DateTime qui retourne un instant qui est approprié pour votre test.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X