如何在C#中对使用DateTime.Now的Values类构造函数进行单元测试?
测试带默认参数的Values构造函数
原始代码与测试困境
我们有一个Values类,构造函数定义如下:
public Values(DateTime dateTimeTo = default){ //some code if (default == dateTimeTo) DateTimeTo = DateTime.Now; else DateTimeTo = dateTimeTo; }
想要验证调用无参构造Values()时,DateTimeTo属性是否被正确赋值,但下面的测试无法正常运行:
[Test] public void Values_DefaultParameters_CreateValuesIstance() { DateTime now = DateTime.Now; Values v = new Values(); Assert.That(v.DateTimeTo, now); }
问题在于两次调用DateTime.Now的时间存在微小差异,导致断言失败。
尝试的依赖注入方案及问题
为了解决这个问题,尝试引入IDateTimeProvider接口来抽象时间获取:
public interface IDateTimeProvider { DateTime getCurrentDateTime(); }
实现了两个类:
- 生产环境用的时间提供类:
public class DateTimeProvider : IDateTimeProvider{ public DateTime getCurrentDateTime() { return DateTime.Now; } }
- 测试用的伪造时间提供类:
public class FakeDateTimeProvider : IDateTimeProvider { public DateTime Date { get; set; } public DateTime getCurrentDateTime() { return Date; } }
但修改构造函数时出现两处错误:
public Values(IDateTimeProvider? dateTimeTo = default){ //some code if (default == dateTimeTo) DateTimeTo = dateTimeTo.getCurrentDateTime(); // 空引用错误 else DateTimeTo = dateTimeTo; // 类型不匹配错误 }
错误原因:
IDateTimeProvider的默认值是null,直接调用getCurrentDateTime()会触发空引用异常- 试图将
IDateTimeProvider类型赋值给DateTime类型的DateTimeTo,类型不匹配
正确的改造方案
1. 修正Values类的构造函数
调整构造函数逻辑,处理dateTimeTo为null的情况,同时确保类型正确:
public class Values { public DateTime DateTimeTo { get; } public Values(IDateTimeProvider? dateTimeProvider = default) { // 未传入时间提供器时,使用默认的DateTimeProvider var provider = dateTimeProvider ?? new DateTimeProvider(); DateTimeTo = provider.getCurrentDateTime(); } }
这样既保留了无参构造的便利性,又通过依赖注入实现了时间获取的可测试性。
2. 编写有效的单元测试
使用FakeDateTimeProvider模拟固定时间,验证构造逻辑:
[Test] public void Values_WithFakeProvider_SetsExpectedDateTime() { // 准备测试用的固定时间 var testTime = new DateTime(2024, 1, 1, 12, 0, 0); var fakeProvider = new FakeDateTimeProvider { Date = testTime }; // 传入伪造的时间提供器 var values = new Values(fakeProvider); // 断言属性值与模拟时间一致 Assert.That(values.DateTimeTo, Is.EqualTo(testTime)); } [Test] public void Values_ParameterlessConstructor_UsesDefaultProvider() { // 替换默认提供器为伪造实例,隔离测试 var testTime = new DateTime(2024, 6, 1); // 可通过给Values类添加静态配置属性实现默认提供器替换 Values.DefaultDateTimeProvider = new FakeDateTimeProvider { Date = testTime }; // 调用无参构造 var values = new Values(); // 断言结果 Assert.That(values.DateTimeTo, Is.EqualTo(testTime)); // 恢复默认提供器,避免影响其他测试 Values.DefaultDateTimeProvider = new DateTimeProvider(); }
如果要支持默认提供器的替换,可给Values类添加静态配置属性:
public class Values { public static IDateTimeProvider DefaultDateTimeProvider { get; set; } = new DateTimeProvider(); public DateTime DateTimeTo { get; } public Values(IDateTimeProvider? dateTimeProvider = default) { var provider = dateTimeProvider ?? DefaultDateTimeProvider; DateTimeTo = provider.getCurrentDateTime(); } }
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

