skip to Main Content

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


  1. 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

    using (Stream stream = await client.OpenReadAsync())
    {
        stream.Seek(0, SeekOrigin.Begin);
        using (StreamReader reader = new StreamReader(stream))
        {
            // read a line
            var record = await reader.ReadLineAsync();
            // ...
            // close the reader when you're done reading
            reader.Close();
        }
        stream.Close();
    }
    

    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.

    Login or Signup to reply.
  2. 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 time client.OpenReadAsync is called.

    I suspect you just need to move the call into a lambda expression:

    client.Setup(c => c.OpenReadAsync())
          .ReturnAsync(() => File.OpenRead("c:\testFile.txt"));
    

    (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.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search