使用 Moq 验证在单独的线程/线程池中发生的方法调用

swd*_*don 3 c# moq

我之前已经成功设置并验证了 Moq 的方法,但不知何故我无法让它工作。我对同一个例外尝试了各种答案,但没有运气。

我已经实现了观察者模式,所以我在嘲笑IObserver<T>

var mock = new Mock<IObserver<T>>();
mock.Setup(s => s.OnCompleted());
Run Code Online (Sandbox Code Playgroud)

这里OnCompleted()看起来像

public void OnCompleted()
{
}
Run Code Online (Sandbox Code Playgroud)

现在在测试中,使用mock,我确实喜欢这样:

// observable is the SUT.
var unsubscriber = observable.Subscribe(mock.Object);
// Cause OnCompleted() to be called: I verified that there's one observer in the observers list in observable and that my break point is correctly hit.

mock.Verify(v => v.OnCompleted(), Times.AtLeastOnce);
unsubscriber.Dispose(); 
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Message: Moq.MockException : 
Expected invocation on the mock at least once, but was never performed: v => v.OnCompleted()

Configured setups: 
IObserver<T> s => s.OnCompleted()
No invocations performed.
Run Code Online (Sandbox Code Playgroud)

编辑:SUT代码

SUT 是使用工厂方法初始化的类。我在这里总结一下相关部分:

有一个初始化方法:

public void InitializeMyClass()
{
   for(var i = 0; i < threads.Count; i++)
   {
       Task.Factory.StartNew(() => Proc())
   }

   this.timer = new Timer(CheckStatus, null, 0, 1000);
}
Run Code Online (Sandbox Code Playgroud)

CheckStatus方法检查启动的线程中的工作负载是否Initializer达到特定状态并引发指示完成的事件:

private void CheckStatus(object status)
{
   // Inspect all background threads.
   // This is simply done by observing a set of values in a concurrent dict<int, bool>.:

   if (!this.concurrentDict.Values.Any(a => a))
   {
       this.NotifyObservers();
       this.timer.Change(Timeout.Infinite, Timeout.Infinite);
   }
}
Run Code Online (Sandbox Code Playgroud)

NotifyObservers()调用该OnCompleted()方法:

private void NotifyObservers()
{
    foreach(o in observers)
    {
        o.OnCompleted();
    }
}
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 5

这可能是线程问题,或者在验证完成时计时器可能尚未调用。这意味着模拟成员在被调用时实际上尚未被调用Verify

在验证方法调用之前,您可能需要稍等一下。

尝试在测试中的 Act 和 Assertion 之间添加延迟,以便为计时器提供足够的时间来完成其任务。

//Arrange

//...

//Act
// observable is the SUT.
var unsubscriber = observable.Subscribe(mock.Object);
// Cause OnCompleted() to be called: I verified that there's one observer in the observers list in observable and that my break point is correctly hit.

await Task.Delay(TimeSpan.FromSeconds(1.5)); //Or some known duration

//Assert    
mock.Verify(v => v.OnCompleted(), Times.AtLeastOnce);
unsubscriber.Dispose(); 
Run Code Online (Sandbox Code Playgroud)