我正在尝试测试我的自定义 IProgress 是否正在执行我期望的操作:每秒“报告”当前状态。
所以我有一些代码,但总是失败。但这是最关键的 - 只有当我测试用例中几乎没有任何代码时,它才会失败。
如果我在测试中再添加一段代码(在存储库中注释掉)..如果我取消注释它就可以了。
这就像..需要启动一些东西..但在检查“断言”之前它还没有完成。
完整小提琴:https://dotnetfiddle.net/qXHzlx
这是代码:
// Arrange.
var reportValue = 0;
var progress = new TimerProgress<int>((int value) => reportValue = value);
await Task.Delay(TimeSpan.FromSeconds(2)); // Need to wait more than 1 second for the report to work.
// Act.
progress.Report(5);
// Assert.
reportValue.ShouldBe(5);
和定制
TimerProgress
:
public class TimerProgress<T> : IProgress<T>
{
private readonly TimeSpan _intervalInMilliseconds;
private readonly IProgress<T> _progress;
private DateTime? _lastReportedOn;
public TimerProgress(Action<T> handler, double intervalInMillieconds = 1000)
{
if (intervalInMillieconds <= 0)
{
throw new ArgumentOutOfRangeException(nameof(intervalInMillieconds));
}
_intervalInMilliseconds = TimeSpan.FromMilliseconds(intervalInMillieconds);
_progress = new Progress<T>(handler);
_lastReportedOn = DateTime.UtcNow;
}
public void Report(T value)
{
var now = DateTime.UtcNow;
if (now - _lastReportedOn > _intervalInMilliseconds)
{
// We're due for a report!
_progress.Report(value);
}
_lastReportedOn = now;
}
}
通过测试用例,我期望:
TimeProgress
实例。 Now
是“记住”。remembered-now
是否超过 1 秒前。 应该是,因为我们已经等了2秒了!所以现在,我们应该“报告”所以我不断收到失败断言,说该值是
0
,而不是 5
。
但是当我在测试中再添加一行代码时,现在的值是
5
。
这是关于时间或事件触发问题吗?
这是关于我的处理程序如何尝试更新处理程序的变量outside,这是 Bad™️ 吗?
人们的建议是说
Progress<T>
实际上在幕后使用了同步上下文,这就是问题所在?
这个问题已经就这个问题进行了很好的讨论。
因为进度是在
SynchronizationContext
上运行的。所以他们建议不要在我自己的班级中使用Progress<T>
。相反,只需调用 Action<T>
directly 这意味着它将在您当前的上下文中发生。
例如(盗用的代码)
public class SynchronousProgress<T> : IProgress<T>
{
private readonly Action<T> action;
public SynchronousProgress(Action<T> action)
{
this.action = action;
}
public void Report(T value)
{
action(value);
}
}