您如何模拟和IAsyncEnumerable?

时间:2019-11-27 14:06:18

标签: c# unit-testing asp.net-core iasyncenumerable

我想对一个方法进行单元测试,该方法调用服务的另一个方法返回IAsyncEnumerable<T>。 我已经创建了服务Mock<MyService>的模拟,我想设置该模拟,但是我不知道该怎么做。可能吗 ?是否有其他方法可以对需要重新调整IAsyncEnumerable

的方法进行单元测试
public async Task<List<String>> MyMethodIWantToTest()
{
  var results = new List<string>();
  await foreach(var item in _myService.CallSomethingReturningAsyncStream())
  {
    results.Add(item);
  }
  return results;
}

3 个答案:

答案 0 :(得分:4)

我建议使用ToAsyncEnumerable中的System.Linq.Async,如Jeroen建议的那样。看来您正在使用Moq,所以它看起来像:

async Task MyTest()
{
  var mock = new Mock<MyService>();
  var mockData = new[] { "first", "second" };
  mock.Setup(x => x.CallSomethingReturningAsyncStream()).Returns(mockData.ToAsyncEnumerable());

  var sut = new SystemUnderTest(mock.Object);
  var result = await sut.MyMethodIWantToTest();

  // TODO: verify `result`
}

答案 1 :(得分:4)

如果您不想做任何特别的事情,例如延迟返回通常是异步枚举的要点,那么您只需创建一个生成器函数即可为您返回值。

public static async IAsyncEnumerable<string> GetTestValues()
{
    yield return "foo";
    yield return "bar";

    await Task.CompletedTask; // to make the compiler warning go away
}

有了它,您可以简单地为您的服务创建一个模拟并测试您的对象:

var serviceMock = new Mock<IMyService>();
serviceMock.Setup(s => s.CallSomethingReturningAsyncStream()).Returns(GetTestValues);

var thing = new Thing(serviceMock.Object);
var result = await thing.MyMethodIWantToTest();
Assert.Equal("foo", result[0]);
Assert.Equal("bar", result[1]);

当然,由于您现在正在使用生成器函数,因此您还可以使其更加复杂并增加实际延迟,甚至包括一些控制收益率的机制。

答案 2 :(得分:0)

这实际上取决于您使用的模拟框架。但是,就像使用Moq

的示例一样,这很简单
var data = new [] {1,2,3,4};
var mockSvc = new Mock<MyService>();
mockSvc.Setup(obj => obj.CallSomethingReturningAsyncStream()).Returns(data.ToAsyncEnumerable());