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

Verify a method call happening in a separate thread/thread pool using Moq

我之前已经成功地设置和验证了 Moq 的方法,但不知何故我无法让它工作。我在同一个异常上尝试了各种答案,但没有成功。

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

var mock = new Mock<IObserver<T>>();
mock.Setup(s => s.OnCompleted());

这里的 OnCompleted() 看起来像

public void OnCompleted()
{
}

现在在测试中,使用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(); 

我收到以下错误:

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.

编辑:SUT 代码

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

有一个初始化方法:

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

   this.timer = new Timer(CheckStatus, null, 0, 1000);
}

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);
   }
}

NotifyObservers()调用OnCompleted()方法:

private void NotifyObservers()
{
    foreach(o in observers)
    {
        o.OnCompleted();
    }
}

这可能是线程问题,或者计时器可能在验证完成时尚未调用。这意味着当 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();