Какой хороший способ перезаписать DateTime.Now во время тестирования?

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

108 голосов | спросил Craig.Nicol 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:02:12 +0400 2008, 17:02:12

11 ответов


0

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

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

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

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

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

ответил Blair Conrad 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:06:28 +0400 2008, 17:06:28
0

Ayende Rahien использует статический метод, который довольно прост ...

public static class SystemTime
{
    public static Func<DateTime> Now = () => DateTime.Now;
}
ответил Anthony Mastrean 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:14:59 +0400 2008, 17:14:59
0

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

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

ответил Mendelt 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:04:32 +0400 2008, 17:04:32
0

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

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

}

В Visual Studio 2012 вы можете добавить сборку Fakes в ваш тестовый проект, щелкнув правой кнопкой мыши сборку, для которой вы хотите создать Fakes /Shims, и выбрав «Add 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);

        }
    }
}
}
ответил mmilleruva 3 Jpm1000000pmFri, 03 Jan 2014 18:56:14 +040014 2014, 18:56:14
0

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

В этом случае вашим внешним является текущий DateTime.

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

ответил Jay Bazuzi 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 09 Sep 2008 18:53:26 +0400 2008, 18:53:26
0

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

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

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

ответил João Angelo 2 FebruaryEurope/MoscowbTue, 02 Feb 2010 15:09:56 +0300000000pmTue, 02 Feb 2010 15:09:56 +030010 2010, 15:09:56
0

Я бы предложил использовать шаблон 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);
    } 
}

Подробно описано здесь: http://www.lesnikowski.com/blog/index.php/тестирование-дата и время, теперь /

ответил Pawel Lesnikowski 2 FebruaryEurope/MoscowbTue, 02 Feb 2010 14:52:08 +0300000000pmTue, 02 Feb 2010 14:52:08 +030010 2010, 14:52:08
0

Простой ответ: ditch System.DateTime :) Вместо этого используйте NodaTime и его библиотеку тестирования: NodaTime.Testing .

Дополнительная информация:

ответил tugberk 18 22014vEurope/Moscow11bEurope/MoscowTue, 18 Nov 2014 15:23:59 +0300 2014, 15:23:59
0

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

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

ответил Daren Thomas 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:06:45 +0400 2008, 17:06:45
0

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

public interface IDateTimeTools
{
    DateTime Now { get; }
}

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

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

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

 введите описание изображения здесь

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

Install-Package -Id DateTimePT -ProjectName Project

А код для Nuget находится здесь .

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

ответил Pawel Wujczyk 27 J0000006Europe/Moscow 2018, 16:43:42
0

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

например.

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

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

Изменить

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

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

ответил Rob Cooper 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2008 17:05:53 +0400 2008, 17:05:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132