I’m trying to mock a read operation by using file stream in unit test. Below is the simplified code in application.
using (var stream = await client.OpenReadAsync())
{
using (StreamReader reader = new StreamReader(stream))
{
// read a line
record = await reader.ReadLineAsync();
}
}
This is the mock
var client = new Mock<Client>();
//return file stream when read is performed on mocked client
client.setup( c => c.openReadAsync()).ReturnAsync(File.Open("c:\testFile.txt", FileMode.Open, FileAccess.Read))
This works well when the stream is read for the first time. I have a logic where the same stream is read second time based on a condition. So the code above for reading stream is repeated in another place and it throws "stream not readable" error.
My understanding is whenever the client.OpenReadAsync() is called, a new file stream should be returned based on the mock condition. How do I fix this ? Can I use any other stream to overcome this ?
Update:
Based on Matt’s suggestion, the below code for first read makes the second read works well, but client.OpenReadAsync() and stream reader are not closed, which I’m not sure cause any issues
var stream = await client.OpenReadAsync()
using (StreamReader reader = new StreamReader(stream,**leaveOpen: true**))
{
// read a line
record = await reader.ReadLineAsync();
}
The openReadAsync() reads from Azure storage. During the actual run everything works fine. In the actual run , the client returns this stream. Probably, if I find a way to mock the same stream, things might work.
2
Answers
If you’re reading a stream, it tracks the read position. If the end is reached but you’re trying to read further, you’re getting an error.
Make sure that you set the read position to 0 before you read it again, like
stream.Seek(0, SeekOrigin.Begin);
You can try a pattern like
Note: After discussing this with Jon Skeet, we came to the conclusion that the behaviour you have observed is a bug (details see below in the comments). Opening a "fresh" stream should always start at position 0.
Hence, this is a workaround – you should analyze the issue further to find out where the root cause is.
Update: In this particular case, Jon found that the unwanted side effect is coming from the mock setup which doesn’t seem to create a fresh stream every time and hence the read position isn’t being reset properly.
You’re very welcome to let us know what you found out and update us on your testing.
I believe the problem is that you’re returning the same task each time your mock is called – you’re calling
File.Open
once, and returning that same task every timeclient.OpenReadAsync
is called.I suspect you just need to move the call into a lambda expression:
(You may need to tweak that a bit – you didn’t provide a minimal reproducible example, and we don’t know which version of Moq you’re using, so it’s hard for me to test the exact syntax you’ll need – but the principle is that you need to open the file separately on every call.)