Модульное тестирование для свойства, которое имеет значение system.DateTime С#

Как написать модульное тестирование для следующего блока кода, где

public class Hotel
{
    DateTime CLOSING_TIME // imported from somewhere else
    public bool IsOpen
    {
        get
        {
            return DateTime.Now <= CLOSING_TIME
        }
    }
}

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

[TestFixture]
public void ShouldBeOpen()
{
    var Hot= new Hotel();
    Assert.True(Hot.IsOpen); 
}

[TestFixture]
public void ShouldBeOpen()
{
    var Hot= new Hotel();
    Assert.False(Hot.IsOpen); 
}

Может кто-нибудь помочь?


person user1931544    schedule 26.07.2018    source источник
comment
Возможный дубликат Какой хороший способ перезаписать DateTime.Now во время тестирования?   -  person FCin    schedule 26.07.2018
comment
вам следует рассмотреть возможность использования только TimeOfDay из DateTime или вы ежедневно обновляете время закрытия?   -  person Marco Forberg    schedule 26.07.2018


Ответы (2)


У вас много проблем с тестами:

  • Они недетерминированы (как вы уже заметили), потому что зависят от времени.
  • Они избыточны (второй тест точно такой же, как и первый)
  • Судя по введенной вами семантике, вам нужно работать со временем, а не с полным DateTime, которое вы не можете выразить в IsOpen

Я также лично считаю, что такой тривиальный тест бесполезен, потому что он не добавляет никакой ценности по сравнению с этим тестом:

[TestFixture]
public void ShouldBeOpen()
{
    Assert.True(DateTime.Now <= CLOSING_TIME);
}

Однако небольшое изменение в IsOpen может улучшить как вашу реализацию, так и код тестирования и сохранить ваш код простым (нет необходимости заглушать/издеваться над системной датой):

public sealed class Hotel
{
    public bool IsOpenAt(DateTime time) => time.TimeOfDay <= CLOSING_TIME.TimeOfDay;
}

С такими тестами, которые действительно проверяют некоторые бизнес-утверждения:

[TestFixture]
public void ShouldBeOpen()
{
    Assert.True(new Hotel().IsOpenAt(CLOSING_TIME));
}

[TestFixture]
public void ShouldNotBeOpen()
{
    Assert.False(new Hotel().IsOpenAt(CLOSING_TIME.AddMinutes(1)));
}

Примечание

Есть кое-что, о чем я хотел бы поговорить, хотя и не просил: CLOSING_TIME. Это поле кажется мне подозрительным. Действительно, я подозреваю, что то, как вы устанавливаете это значение, вводит ненужную связь что-то с Hotel. Если это не так, я бы посоветовал вам запросить это значение в качестве параметра конструктора для достижения лучшей развязки:

public sealed class Hotel
{
    private readonly DateTime _closingTime;
    public Hotel(DateTime closingTime) => _closingTime = closingTime;
    public bool IsOpenAt(DateTime time) => time.TimeOfDay <= _closingTime.TimeOfDay;
}
person Spotted    schedule 26.07.2018

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

    class Hotel
    {
        public DateTime ClosingTime = DateTime.ParseExact("17:00:00", "HH:ii:ss", CultureInfo.InvariantCulture);
        public IStubClock Clock;
        public bool IsOpen
        {
            get
            {
                return Clock.Now.TimeOfDay <= ClosingTime.TimeOfDay;
            }
        }
        public Hotel(IStubClock clock)
        {
            Clock = clock;
        }
    }

Используя этот интерфейс, вы можете издеваться над любой структурой DateTime.Now.

    public interface IStubClock
    {
        DateTime Now { get; }
    }

Поддельный вариант

    public class FakeClock : IStubClock
    {
        private DateTime _now;
        public DateTime Now
        {
            get
            {
                return _now;
            }
        }

        public FakeClock(DateTime now)
        {
            _now = now;
        }
    }

И реальный вариант

    public class RealClock : IStubClock
    {
        public DateTime Now
        {
            get
            {
                return DateTime.Now;
            }
        }
    }

а затем вы можете использовать их в своих тестах, делая что-то вроде этого

    class Program
    {
        static void Main(string[] args)
        {
            IStubClock fakeClock = new FakeClock(new DateTime(1, 1, 1, 10, 0, 0)); //time is set to 10am
            IStubClock realClock = new RealClock(); //time is set to whatever the time now is.
            Hotel hotel1 = new Hotel(fakeClock); //using fake time
            Hotel hotel2 = new Hotel(realClock); //using the real time
        }
    }
person Dan Scott    schedule 26.07.2018