As you have seen I have published 3 articles series on How to make integration test for Azure Storage. Mean while a few of you wrote to me on how can I make mocked unit test of the same methods that I presented in my article part 2.
As you remember, we created a empty solution in Visual Studio 2022 and call it AzureBlobStorageApp
. Than I will create my first library project and call it AzureBlobStorageApp
.
I add Azure.Storage.Blobs
NuGet package to my project.
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
</ItemGroup>
Now lets create a class AzureBlobStorage.cs
with a constructor that takes connection string and container name.
public AzureBlobStorage(string connectionString, string container)
{
_blobContainerClient = new BlobContainerClient(connectionString, container);
_blobContainerClient.CreateIfNotExists();
}
We will create 3 methods that Read, Create and Delete text file.
public async Task<string> ReadTextFile(string filename)
{
var blob = _blobContainerClient.GetBlobClient(filename);
if (!await _blobContainerClient.ExistsAsync()) return string.Empty;
var reading = await blob.DownloadStreamingAsync();
StreamReader reader = new StreamReader(reading.Value.Content);
return await reader.ReadToEndAsync();
}
public async Task CreateTextFile(string filename, byte[] data)
{
var blob = _blobContainerClient.GetBlobClient(filename);
await using var ms = new MemoryStream(data, false);
await blob.UploadAsync(ms, CancellationToken.None);
}
public async Task DeleteTextFile(string filename)
{
var blobClient = _blobContainerClient.GetBlobClient(filename);
await blobClient.DeleteAsync();
}
public int NumberOfBlobs()
{
var blobClient = _blobContainerClient.GetBlobs();
return blobClient.Count();
}
For the above methods, I have created an interface call it IAzureBlobStorage
that contains the methods and signature, as we need this interface later for mocking of our methods:
public interface IAzureBlobStorage
{
public Task<string> ReadTextFile(string filename);
public Task CreateTextFile(string filename, byte[] data);
public Task DeleteTextFile(string filename);
public int NumberOfBlobs();
}
Now we create a new xunit test project and call it AzureBlobStorageApp.UnitTests
Check and eventually adding following dependencies, and of course add reference to our AzureBlobStorageApp
project as well.
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
<PackageReference Include="Docker.DotNet" Version="3.125.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AzureBlobStorageApp\AzureBlobStorageApp.csproj" />
</ItemGroup>
Lets create a class AzureBlobStorageMockUnitTests
, and creating our first unit test:
[Fact]
public async Task AzureBlobStorageTest()
{
// Arrange
var mock = new Mock<IAzureBlobStorage>();
mock
.Setup(azureBlobStorage => azureBlobStorage.CreateTextFile(It.IsAny<string>(), It.IsAny<byte[]>()))
.Callback(() =>
{
mock.Setup(azureBlobStorage => azureBlobStorage.ReadTextFile(It.IsAny<string>()))
.Returns(async () => await Task.FromResult(Content));
mock.Setup(azureBlobStorage => azureBlobStorage.NumberOfBlobs())
.Returns(1);
});
mock
.Setup(azureBlobStorage => azureBlobStorage.DeleteTextFile(It.IsAny<string>()))
.Callback(() =>
{
mock.Setup(azureBlobStorage => azureBlobStorage.ReadTextFile(It.IsAny<string>()))
.Returns(async () => await Task.FromResult(string.Empty));
mock.Setup(azureBlobStorage => azureBlobStorage.NumberOfBlobs())
.Returns(0);
});
// Act
await mock.Object.CreateTextFile("file.txt", Encoding.UTF8.GetBytes(Content));
var readTextFile = await mock.Object.ReadTextFile("file.txt");
var filesCount = mock.Object.NumberOfBlobs();
// Assert
Assert.Equal(Content, readTextFile);
Assert.Equal(1, filesCount);
// Finalizing Act
await mock.Object.DeleteTextFile("file.txt");
var readTextFileAfterDelete = await mock.Object.ReadTextFile("file.txt");
var filesCountAfterDelete = mock.Object.NumberOfBlobs();
// Finalizing Assert
Assert.Equal(string.Empty, readTextFileAfterDelete);
Assert.Equal(0, filesCountAfterDelete);
}
My test might be not very innovative, but it is to give some idea how to do mocking. So what we do here is we say each time we Call CreateTextFile
, a result of that we need method ReadTextFile
return us the file that we created. And count up the amount of files in our blob.
The same when we call DeleteTextFile
, we do the opposite. By delete the file content and decrease our count to zero.
This way, we ensure if some one change our methods and not following our test expectation, by change the signature, or returning some not expected value, our test will break.
Now lets fire up our test, remember your azurite docker should be up and running.
Congrats, now you have just learned how to mock unit test your methods that for Azure Blob Storage on local environment.
Download the project from GitHub.
Conclusion
In this article, we learned to how to mock unit test Azure Blob Storage methods.
If you really need to make integration test with out specific Azure Cloud account or Environment, see my 3 articles series.
I would also in addition reference to this article from Microsoft.
Graphic credit
The banner in this article is design by my ❤️ 15 years son Jassin Fahmi 😀.
Hi,
Anyway to do the same test but by writing test stubs?