• 框架重构:测试中的DateTime.Now


    存在的问题

    • DateTime.Now是C#语言中获取计算机的当前时间的代码;
    • 但是,在对使用了DateTime.Now的方法进行测试时,由于计算机时间的实时性,期望值一直在变化。如:计算年龄。
    public static class DateTimeExtensionMethods
    {
        public static int Age(this DateTime date)
        {
            DateTime now = DateTime.Now;
            ......
        }
    }
    
    new DateTime(2008,8,12).Age().ShouldEqual(???);
    

    解决方案一:给Age方法增加一个参数,将当前时间传进去

    // method
    public static int Age(this DateTime date, DateTime now){...}
    // test
    new DateTime(2008,8,12).Age(new DateTime(2017,8,13)).ShouldEqual(9);
    

    缺点:

    • 显示传入当前时间麻烦
    • 多一个参数,维护成本也会增加
    • 感觉怪怪的,不符合习惯

    解决方案二:使用IoC框架实现

    public interface ISystemClock
    {
        DateTime Now { get; }
    }
    public class SystemClock : ISystemClock
    {
        public DateTime Now 
        { 
            get { return DateTime.Now; } 
        }
    }
    
    
    public static class DateTimeExtensionMethods
    {
        public static int Age(this DateTime date)
        {
            DateTime now = IoC.Get<ISystemClock>().Now;
            ......
        }
    }
    
    // 真实系统
    IoC.Register<ISystemClock, SystemClock>();
    
    // test
    var mock = MockRepository.GenerateMock<ISystemClock>();
    mock.Stub(x=>x.Now).Return(new DateTime(2017,8,13);
    IoC.Register<ISystemClock>(mock);
    

    缺点

    • 需要使用IoC框架
    • 操作繁琐,代码量有点多

    解决方案三:使用委托(当前最佳方案)

    public static class SystemClock
    {
        public static Func<DateTime> Now = () => DateTime.Now;
    }
    
    public static class DateTimeExtensionMethods
    {
        public static int Age(this DateTime date)
        {
            DateTime now = SystemClock.Now();
            int age = now.Year - date.Year;
            if (now.Month == date.Month)
                age = (now.Day < date.Day) ? age - 1 : age;
            else if (now.Month < date.Month)
                age = age - 1;
            return age;
        }
    }
    
    [Subject(typeof(DateTime), "Age")]
    public class when_convert_birthday_to_age
    {
        Establish context = () => SystemClock.Now = () => new DateTime(2013, 8, 25);
    
        public class with_yesterday_is_birthday
        {
            Because of = () => result = new DateTime(1980, 8, 24).Age();
            It 应该计算出正确的年龄 = () => result.ShouldEqual(33);
        }
            
        public class with_today_is_birthday
        {
            Because of = () => result = new DateTime(1980, 8, 24).Age();
            It 应该计算出正确的年龄 = () => result.ShouldEqual(33);
        }
    
        public class with_tomorrow_is_birthday
        {
            Because of = () => result = new DateTime(1980, 8, 26).Age();
            It 应该计算出正确的年龄 = () => result.ShouldEqual(32);
        }
    
        private static int result;
    }
    

    You can implement ICleanupAfterEveryContextInAssembly to perform cleanup after every context.
    machine.specifications官网

    // 每个测试执行完后,需把SystemClock.Now还原
    public class ResetTheClock : ICleanupAfterEveryContextInAssembly
    {
        public void AfterContextCleanup()
        {
            SystemClock.Now = () => DateTime.Now;
        }
    }
    
  • 相关阅读:
    python第八课
    python第七课
    python第六课
    python第五课
    Python基础30类-内置函数实现迭代器协议
    Python基础29类-内置函数(__format__,__slots__,__doc__,__module__,__del__,__call__)
    Python基础28类-内置函数(__getattribute__,__getitem__,__setitem__.__delittem__)
    Python基础27类-包装、组合方式授权、判断对象类型的方法
    Python基础26类-内置函数__setattr__,__getattr__,__delattr__
    Python基础25类-反射
  • 原文地址:https://www.cnblogs.com/tuty/p/6751603.html
Copyright © 2020-2023  润新知