Mocking a CloudBlockBlob and have it return a stream
Asked Answered
G

1

13

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?

Grigson answered 8/3, 2019 at 13:12 Comment(3)
Those are two different streams. Grab the stream passed in the argument of the mock and copy the test stream over.Spoliate
Isn't DownloadToStreamAsync copying my sample stream over to ms then?Grigson
No it is not. And also that method returns a plain Task. Not the stream itselfSpoliate
S
13

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

//...
Spoliate answered 8/3, 2019 at 13:22 Comment(3)
tnx, that works indeed! Guess I have some reading up to do on MoqGrigson
I am getting excetpion ....Invalid callback. Setup on method with parameters (Stream,AccessCondition,FileRequestOptions,OperationContext) cannot invoke callback with parameters (Stream). In my case I am mocking CloudFileRabbet
Missed to add other parameters to the callback method.. #61499258Rabbet

© 2022 - 2024 — McMap. All rights reserved.