【问题标题】:Unit testing for property which has sytem.DateTime value C#具有 sytem.DateTime 值 C# 的属性的单元测试
【发布时间】:2018-07-26 12:42:24
【问题描述】:

如何为以下代码块编写单元测试

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

有人可以帮忙吗?

【问题讨论】:

标签: c# vs-unit-testing-framework


【解决方案1】:

你的测试有很多问题:

  • 它们是不确定的(正如您已经注意到的),因为它们依赖于时间
  • 它们是多余的(第二次测试与第一次测试完全相同)
  • 从您输入的语义看来,您需要处理时间而不是完整的 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。这个字段在我看来可疑。实际上,我怀疑您设置此值的方式会引入与Hotelsomething 不必要的耦合。如果不是这样,我建议你请求这个值作为构造函数的参数,以实现更好的解耦:

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

【讨论】:

    【解决方案2】:

    我将下面的代码放在一起模拟一个可用于单元测试的时间接口。使用它,您可以将时间设置为您指定的实际时间或假时间。我在单元测试时经常使用这种方法。它被称为依赖注入或构造函数注入,对于单元测试非常有用。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 2018-09-04
      • 2012-08-29
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      相关资源
      最近更新 更多