【问题标题】:Config value in unit test never gets set单元测试中的配置值永远不会被设置
【发布时间】:2020-11-25 07:25:20
【问题描述】:

我的 C# 应用程序有一个 HealthHandler 类,它在其构造函数中将配置作为参数。

public class HealthHandler : IHeartbeatCheckHealthHandler
{
    private readonly IApplicationTime _applicationTime;
    private readonly IConfig _config;
 
    private DateTime _time = DateTime.UtcNow;
    private readonly TimeSpan _timespan;
 
    public bool IsHealthy => (_time.Add(_timespan) > _applicationTime.UtcNow);
 
    public HealthHandler([NotNull] IConfig healthcheckOptions)
    {
        _applicationTime = new StandardManualTimer(new DateTime?(DateTime.UtcNow));
        _config = healthcheckOptions ?? throw new ArgumentNullException(nameof(healthcheckOptions));
        _timespan = TimeSpan.FromSeconds(_config.HeartbeatTimespan);
    }
 
    public void OnMessage(int value)
    {
        if (value == 10)
        {
            _time = _applicationTime.UtcNow;
        }
    }
}

我正在尝试对此进行单元测试。到目前为止,这是我所得到的:

public class HealthcheckTest
{
    private IConfig _config;
 
    public HealthcheckTest()
    {
        _config = new FeedConfigOptions();
        _config.HeartbeatTimespan = 5;
    }
 
    [Fact]
    public void TestKafkaHealthStateUpdates()
    {
            
        HealthHandler healthHanler = new HealthHandler(_config);
        healthHanler.OnMessage(2);
        healthHanler.IsHealthy.Should().Be(true);
        Thread.Sleep(6000);
        healthHanler.IsHealthy.Should().Be(false);
        healthHanler.OnMessage(10);
        healthHanler.IsHealthy.Should().Be(true);
    }
 
}

每个healthHanler.IsHealthy.Should().Be(); 调用都返回true。看来我的测试从未将 HeartbeatTimespan 设置为 5,它始终默认为 0。

我的 StandardManualTimer 如下所示:

public class StandardManualTimer : ManualTimer<DateTime>, IApplicationTime
{
    public StandardManualTimer(DateTime? initial = null, TimerGranularity? granularity = null)
      : base(initial ?? DateTime.UtcNow, granularity)
    {
    }
 
    public void AdvanceBy(TimeSpan timeSpan) => this.AdvanceTo(this.Now.Add(timeSpan));
 
    public DateTime UtcNow => this.Now;
  }

下面是手动定时器:

public class ManualTimer<TPriority> : IManualTimer<TPriority>, ITimer<TPriority>, IDisposable
    where TPriority : IComparable<TPriority>
{
    private volatile bool _isDisposed;
 
    [NotNull]
    public TPriority Next { get; private set; }
 
    public TimerGranularity Granularity { get; }
 
    [NotNull]
    public TPriority Previous { get; private set; }
 
    public bool IsCancelled { get; private set; }
 
    public ManualTimer(TPriority initial, TimerGranularity? granularity = null)
    {
      this.Next = (object) initial != null ? initial : throw new ArgumentNullException(nameof (initial), "The initial time cannot be null");
      this.Granularity = granularity.GetValueOrDefault();
      this.Previous = initial;
      this.Now = initial;
      this.IsCancelled = true;
    }
 
    public void Dispose() => this._isDisposed = true;
 
    public void Cancel() => this.IsCancelled = true;
 
    public TimerChangeResponse RequestNext(TPriority next)
    {
      if (this._isDisposed)
        return TimerChangeResponse.Disposed;
      if (next.CompareTo(this.Now) <= 0)
        return TimerChangeResponse.FailedRetry;
      this.IsCancelled = false;
      this.Next = next;
      return TimerChangeResponse.Scheduled;
    }
 
    public void AdvanceTo(TPriority newNow)
    {
      if ((object) newNow == null)
        throw new ArgumentNullException(nameof (newNow), "Now cannot be null");
      if (this.Granularity == TimerGranularity.Fine)
      {
        while (!this.IsCancelled && this.Next.CompareTo(newNow) < 0 && this.Next.CompareTo(this.Previous) > 0)
          SetNow(this.Next);
      }
      if (this.Now.CompareTo(newNow) >= 0)
        return;
      SetNow(newNow);
 
      void SetNow(TPriority value)
      {
        this.Now = value;
        if (this.IsCancelled || this.Now.CompareTo(this.Next) < 0 || this.Next.CompareTo(this.Previous) <= 0)
          return;
        this.Previous = value;
        EventHandler nextFired = this.NextFired;
        if (nextFired == null)
          return;
        nextFired((object) this, EventArgs.Empty);
      }
    }
 
    public event EventHandler NextFired;
 
    [NotNull]
    public TPriority Now { get; private set; }
}

如何在我的测试中解决这个问题?

【问题讨论】:

    标签: c# .net unit-testing


    【解决方案1】:

    _applicationTime 用当前时间初始化:

    _applicationTime = new StandardManualTimer(new DateTime?(DateTime.UtcNow));
    

    _applicationTime.UtcNow返回初始化值,则Thread.Sleep不影响_applicationTime.UtcNow。你需要使用_applicationTime.AdvanceTo 喜欢:

    注入IApplicationTime

    public class HealthHandler : IHeartbeatCheckHealthHandler
    {
        private readonly IApplicationTime _applicationTime;
        private readonly IConfig _config;
    
        private DateTime _time = DateTime.UtcNow;
        private readonly TimeSpan _timespan;
    
        public bool IsHealthy => (_time.Add(_timespan) > _applicationTime.UtcNow);
    
        public HealthHandler([NotNull] IConfig healthcheckOptions, IApplicationTime applicationTime)
        {
            _applicationTime = applicationTime;
            _config = healthcheckOptions ?? throw new ArgumentNullException(nameof(healthcheckOptions));
            _timespan = TimeSpan.FromSeconds(_config.HeartbeatTimespan);
        }
    
        public void OnMessage(int value)
        {
            if (value == 10)
            {
                _time = _applicationTime.UtcNow;
            }
        }
    }
    

    并修改测试:

    [Fact]
    public void TestKafkaHealthStateUpdates()
    {
        var app = new StandardManualTimer(DateTime.UtcNow);
        HealthHandler healthHanler = new HealthHandler(_config, app);
        healthHanler.OnMessage(2);
        healthHanler.IsHealthy.Should().Be(true);
        Thread.Sleep(6000);
        app.AdvanceTo(DateTime.UtcNow);
        healthHanler.IsHealthy.Should().Be(false);
        healthHanler.OnMessage(10);
        healthHanler.IsHealthy.Should().Be(true);
    }
    

    奖励:您还可以模拟IApplicationTime

    【讨论】:

    • 对不起,也许是个愚蠢的问题,你是说我需要这样做,以便可以更新 _applicartionTime?
    • 我怀疑问题来自StandardManualTimer。可以在您的问题中添加代码吗?
    • 我将其添加到问题中。
    • 这也被添加到问题中。
    猜你喜欢
    • 1970-01-01
    • 2011-07-23
    • 2021-09-11
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    相关资源
    最近更新 更多