c# - 单元测试中的配置值永远不会被设置
问题描述
我的 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; }
}
如何在我的测试中解决这个问题?
解决方案
_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
。
推荐阅读
- python - 在带有置信区间的python中绘制偏自相关和互相关图
- sql-server - SQL Server 中关键字“DEFAULT”附近的语法不正确
- ios - 在ios swift中根据“id”参数获取不同的值
- sql - 为什么 AES_ENCRYPT 在 SQL 服务器上不起作用
- aws-lambda - 使用 CF 模板创建 AWS::Pinpoint::PushTemplate
- qt - 在 RowLayout 中使用其他元素标记自动换行
- mongodb - 如何使用 Mongoose 更新嵌套数组?
- java - java类外的访问接口
- ios - 扩展导致 Swift 中的清除按钮出现问题
- javascript - React/Javascript 在实现字典时遇到问题