Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a CloudBlockBlob and have it return a stream

I'm trying to Moq an Azure CloudBlockBlob and have it return a Stream so that I can test whether my BlobStorage repository is handling the output correctly.

But somehow the returned stream is always empty.

Unit test code:

//....

var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write("sample data");
writer.Flush();
stream.Position = 0;

var blobMock = new Mock<CloudBlockBlob>(new Uri("http://tempuri.org/blob"));
blobMock
    .Setup(m => m.ExistsAsync())
    .ReturnsAsync(true);
blobMock
    .Setup(m => m.DownloadToStreamAsync(It.IsAny<MemoryStream>()))
    .Returns(Task.FromResult(stream));

//....

Repository code:

//....

var blob = GetContainer(container).GetBlockBlobReference(name);
if (await blob.ExistsAsync())
{
    var ms = new MemoryStream();
    await blob.DownloadToStreamAsync(ms);
    ms.Seek(0, SeekOrigin.Begin);
    return ms;
}

//....

So my returned MemoryStream ms is always an empty stream and not the stream object I'm using in my Moq Returns() method.

How can I have that blob return my sample stream?

like image 206
Reinder Wit Avatar asked Mar 08 '19 13:03

Reinder Wit


Video Answer


1 Answers

Those are two different streams. Grab the stream passed in the argument of the mock in a Callback and copy the test stream over.

For example

//....
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write("sample data");
writer.Flush();
stream.Position = 0;

var blobMock = new Mock<CloudBlockBlob>(new Uri("http://tempuri.org/blob"));
blobMock
    .Setup(m => m.ExistsAsync())
    .ReturnsAsync(true);
blobMock
    .Setup(m => m.DownloadToStreamAsync(It.IsAny<Stream>()))
    .Callback((Stream target) => stream.CopyTo(target)) //<---Something like this
    .Returns(Task.CompletedTask);
//....

The mock does not actually return a stream. It is suppose to act on the stream, which is why the callback is needed to replicated the expected behavior.

Take note

Copying begins at the current position in the current stream, and does not reset the position of the destination stream after the copy operation is complete.

So in that case you might want to reset it if the intention was to read from the target

//...

.Callback((Stream target) => {
    stream.CopyTo(target);
    target.Position = 0;
})

//...
like image 135
Nkosi Avatar answered Oct 22 '22 16:10

Nkosi