使用Moq模拟事件(基于事件的异步模式)-如何在UT中对事件做出反应

本文关键字:事件 UT 模式 模拟 Moq 于事件 使用 异步 | 更新日期: 2023-09-27 17:54:59

我有一个通过事件驱动的异步模式公开异步操作的服务。

public interface IService
{
    void DoAsync(int param);
    event DoCompleted;
}

有另一个类依赖于IService服务对象

public class Foo
{
  private IService _service;
  public EventHandler CalculationComplete;
  public void Foo(IService service) {_service = service};
  public int Calculated;
  public void CalculateAsync(int param)
  {
    //Invoke _service.DoAsync(param)
    //(...)
  }
}

基本上在调用foo之后。CalculateAsyc CalculationComplete应该通知消费者计算完成。

问题是如何在单元测试Foo时模拟isservice ?我用的是Moq。更具体地说,如何使unittest等待CalculationComplete事件和相应的反应?

使用Moq模拟事件(基于事件的异步模式)-如何在UT中对事件做出反应

很难知道你在这里要测试什么,所以我不能给你一个100%准确的样本。您的示例代码似乎缺少相当多的细节…我填补了一些缺失的部分,但还有更多的问题。

在任何情况下,我用来等待事件的方法是一个信号量。我喜欢在简单的场合使用AutoResetEvent。

public class Foo
{
  private IService _service;
  public EventHandler CalculationComplete;
  public Foo(IService service) 
  {
    _service = service;
    _service.DoCompleted += (o,e) => 
    {
            Calculated = e.Result;
        if(CalculationComplete != null) { CalculationComplete(this, new EventArgs()); }
    };
  }
  public int Calculated;
  public void CalculateAsync(int param)
  {
    _service.DoAsync(param);
  }
}

public interface IService
{
    void DoAsync(int param);
    event EventHandler<DoResultEventArgs> DoCompleted;
}
public class DoResultEventArgs : EventArgs
{
    public int Result { get; set; }
}
[TestMethod]
public void CalculateAsync_CallsService_CalculatedIsPopulated()
{
    //Arrange
    Mock<IService> sMock = new Mock<IService>();
    sMock.Setup(s => s.DoAsync(It.IsAny<int>()))
             .Raises(s => s.DoCompleted += null, new DoResultEventArgs() { Result = 324 });
    Foo foo = new Foo(sMock.Object);
    AutoResetEvent waitHandle = new AutoResetEvent(false);
    foo.CalculationComplete += (o,e) => waitHandle.Set();
    //Act
    foo.CalculateAsync(12);
    waitHandle.WaitOne();
    //Assert
    Assert.IsEqual(foo.Calculated, 324);
}
在没有更多信息的情况下,这是我能做的最好的了。