Какой хороший способ перезаписать дату и время?Сейчас, во время тестирования?

StackOverflow https://stackoverflow.com/questions/43711

Вопрос

У меня есть некоторый код (C #), который использует сегодняшнюю дату для правильного расчета событий в будущем.Если я использую сегодняшнюю дату в тестировании, мне придется повторить вычисление в тесте, что кажется неправильным.Каков наилучший способ присвоить дате известное значение в рамках теста, чтобы я мог проверить, является ли результат известным значением?

Это было полезно?

Решение

Я предпочитаю, чтобы классы, использующие time, фактически полагались на интерфейс, такой как

interface IClock
{
    DateTime Now { get; } 
}

С конкретной реализацией

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

Затем, если вы хотите, вы можете предоставить любой другой тип часов, который вы хотите для тестирования, например

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

Могут возникнуть некоторые накладные расходы при предоставлении часов классу, который полагается на него, но это может быть обработано любым количеством решений для внедрения зависимостей (с использованием инверсии контейнера управления, простого старого внедрения конструктора / установщика или даже Статический шаблон Шлюза).

Другие механизмы доставки объекта или метода, которые обеспечивают желаемое время, также работают, но я думаю, что главное - избегать сброса системных часов, поскольку это просто вызовет проблемы на других уровнях.

Кроме того, используя DateTime.Now и включение этого в ваши вычисления не просто кажется неправильным - это лишает вас возможности проверять определенное время, например, если вы обнаружите ошибку, которая происходит только около полуночи или по вторникам.Использование текущего времени не позволит вам протестировать эти сценарии.Или, по крайней мере, не тогда, когда ты захочешь.

Другие советы

Айенде Рахиен использование статический метод, который довольно прост...

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

Я думаю, что создание отдельного класса clock для чего-то простого, например, получения текущей даты, немного излишне.

Вы можете передать сегодняшнюю дату в качестве параметра, чтобы вы могли ввести другую дату в тест.Это имеет дополнительное преимущество, поскольку делает ваш код более гибким.

Использование подделок Microsoft для создания прокладки - действительно простой способ сделать это.Предположим, у меня был следующий класс:

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

}

В Visual Studio 2012 вы можете добавить сборку Fakes в свой тестовый проект, щелкнув правой кнопкой мыши на сборке, для которой вы хотите создать Fakes / прокладки, и выбрав "Добавить сборку Fakes".

Adding Fakes Assembly

Наконец, вот как будет выглядеть тестовый класс:

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

        }
    }
}
}

Ключом к успешному модульному тестированию является развязка.Вы должны отделить свой интересный код от его внешних зависимостей, чтобы его можно было тестировать изолированно.(К счастью, разработка, основанная на тестировании, приводит к созданию несвязанного кода.)

В этом случае вашим внешним значением является текущая дата-время.

Мой совет здесь состоит в том, чтобы извлечь логику, которая имеет дело с DateTime, в новый метод, класс или что-то еще, что имеет смысл в вашем случае, и передать DateTime.Теперь ваш модульный тест может передавать произвольную дату и время для получения предсказуемых результатов.

Еще один, использующий Microsoft Moles (Платформа изоляции для .NET).

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

Moles позволяет заменить любой метод .NET делегатом.Moles поддерживает статические или невиртуальные методы.Moles полагается на профилировщик от Pex.

Я бы предложил использовать IDisposable pattern:

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

Подробно описано здесь:http://www.lesnikowski.com/blog/index.php/testing-datetime-now/

Простой ответ:отключите систему.Дата-время :) Вместо этого используйте НодаТиме и это тестовая библиотека: NodaTime.Тестирование.

Дальнейшее чтение:

Вы могли бы ввести класс (лучше:способ/делегировать) вы используете для DateTime.Now в тестируемом классе.Иметь DateTime.Now быть значением по умолчанию и устанавливать его при тестировании только для фиктивного метода, который возвращает постоянное значение.

Редактировать: То, что сказал Блэр Конрад (у него есть кое-какой код, на который нужно посмотреть).За исключением того, что я предпочитаю делегаты для этого, поскольку они не загромождают вашу иерархию классов такими вещами, как IClock...

Я так часто сталкивался с этой ситуацией, что создал простой nuget, который предоставляет Сейчас свойство через интерфейс.

public interface IDateTimeTools
{
    DateTime Now { get; }
}

Реализация, конечно, очень проста

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

Итак, после добавления nuget в мой проект я могу использовать его в модульных тестах

enter image description here

Вы можете установить модуль прямо из GUI Nuget Package Manager или с помощью команды:

Install-Package -Id DateTimePT -ProjectName Project

И код для Nuget таков здесь.

Пример использования с Autofac можно найти здесь.

Рассматривали ли вы возможность использования условной компиляции для управления тем, что происходит во время отладки / развертывания?

например ,

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

В противном случае вы хотите предоставить доступ к свойству, чтобы вы могли манипулировать им, все это является частью задачи написания поддающийся проверке код, с которым я сейчас борюсь сам:D

Редактировать

Большая часть меня предпочла бы Подход Блэра.Это позволяет вам "горячо подключать" части кода, чтобы помочь в тестировании.Все это соответствует принципу проектирования инкапсулируйте то, что меняется тестовый код ничем не отличается от производственного, просто его никто никогда не видит извне.

Создание и интерфейс могут показаться большой работой для этого примера (именно поэтому я выбрал условную компиляцию).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top