Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you mock an IAsyncEnumerable?

Tags:

I want to unit test a method that calls another method of a service returning an IAsyncEnumerable<T>. I have created a a mock of my service Mock<MyService> and I want to setUp this mock but I don't know how to do that. Is it possible ? Are there other ways of unit testing a method that calls something retuning an IAsyncEnumerable

public async Task<List<String>> MyMethodIWantToTest() {   var results = new List<string>();   await foreach(var item in _myService.CallSomethingReturningAsyncStream())   {     results.Add(item);   }   return results; } 
like image 333
TechWatching Avatar asked Nov 27 '19 14:11

TechWatching


2 Answers

I recommend using ToAsyncEnumerable from System.Linq.Async, as Jeroen suggested. It seems like you're using Moq, so this would look like:

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` } 
like image 118
Stephen Cleary Avatar answered Sep 29 '22 12:09

Stephen Cleary


If you don’t want to do anything special, e.g. a delayed return which is usually the point of async enumerables, then you can just create a generator function that returns the values for you.

public static async IAsyncEnumerable<string> GetTestValues() {     yield return "foo";     yield return "bar";      await Task.CompletedTask; // to make the compiler warning go away } 

With that, you can simply create a mock for your service and test your object:

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

Of course, since you are now using a generator function, you can also make this more complicated and add actual delays, or even include some mechanism to control the yielding.

like image 26
poke Avatar answered Sep 29 '22 11:09

poke