You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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; // 类型不匹配错误
}

错误原因:

  1. IDateTimeProvider的默认值是null,直接调用getCurrentDateTime()会触发空引用异常
  2. 试图将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:23:11