Hi Everyone!

This post is continuation of how to perform unit and integration tests for Azure Blob Storage using Azurite Test Containers, Moq and xUnit. Over the time, I will updated this page with links to individual posts :

Getting started with testing for Azure Blob Storage : Dependency Injection

This Post - Getting started with testing for Azure Blob Storage : Unit Test with help of Moq

Getting started with testing for Azure Blob Storage : Unit Test with help of FakeItEasy (Alternative to MoQ)

Getting started with testing for Azure Blob Storage : Integration Test with help of TestContainers and Azurite

Getting started with testing for Azure Blob Storage : Mocking Azure Blob/File Storage SDK

Getting started with testing for Azure Blob Storage : Mocking Azure Blob/File Storage SDK with help of FakeItEasy (Alternative to MoQ)

Now, we know the context of the problem and by some extend we tried to solve it. However UploadFileToAzBlob still has a dependency on IServiceProvider.

First thing first, we will try to replace the dependency with IAzBlobService and to make it work, we will refactor the Main method. Actually not much difference, we just introduced another method CreateHostBuilder to create IHostBuilder instance. I have also used ExcludeFromCodeCoverage attribute to exclude configuration methods from code coverage. Below is the code snippet for updated startup -

Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 public class Program
    {
        [ExcludeFromCodeCoverage]
        public static async Task Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            var azBlobService = host.Services.GetRequiredService<IAzBlobService>();
            await UploadFileToAzBlobAsync(azBlobService);

            await host.RunAsync();
        }
        [ExcludeFromCodeCoverage]
         static IHostBuilder CreateHostBuilder(string[] args) =>
           Host
            .CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                services.AddSingleton<IAzBlobService, AzBlobService>();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                      .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
            });

Here is the updated code for UploadFileToAzBlob method, now it does not have any dependency on IServiceProvider instead it has dependency on IAzBlobService.

Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
public static async Task UploadFileToAzBlobAsync(IAzBlobService azBlobService)
        {
            var result = await azBlobService.UploadFileToAzBlobAsync("samplefile.txt");

            if (result)
            {
                Console.WriteLine("File uploaded successfully");
            }
            else
            {
                Console.WriteLine("File upload failed");
            }
        }

So, Coming back to the original heading of the post, How can we perform unit test for UploadFileToAzBlob method? We refactored it lot, will it help us to perform unit test? Let’s find out.

We will use two very popular tool / framework for this post.

  1. xUnit.net - Unit testing tool for the .NET
  2. Moq - Mocking framework for .NET

Let’s start with xUnit.net. We will create a new project Sundry.AzStorageTest.UnitTest and add Moq as nuget packages. Here is the code snippet of csproj for test project

SystemUnderTest.UnitTest.csproj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
    <PackageReference Include="Moq" Version="4.18.3" />
    <PackageReference Include="xunit" Version="2.4.2" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="coverlet.collector" Version="3.1.2">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\SystemUnderTest\SystemUnderTest.csproj" />
  </ItemGroup>

</Project>

Nothing fancy here, however if you see the project reference path, we have added the reference to the project we are going to test. This is important, because we will be using the code from the project we are going to test. Also we use ..\..\src folder structure, because we have the project we are going to test in src folder and test project in tests folder like this -

Writing Unit Test

We have setup all tools and framework and ready to write our first test case. Let’s try to understand what are the scenarios we want to test. We have two scenarios in our mind, one is when the file is uploaded successfully and another is when the file upload fails. We will write test cases for each scenario.

If you notice UploadFileToAzBlob writes the result to console. so,let’s prepare our UnitTests.cs to handle this -

UnitTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
using Moq;
using SystemUnderTest.Interface;

namespace SystemUnderTest.UnitTest;

public class UnitTests
{
   private readonly StringWriter Output = new StringWriter();
    public UnitTests()
    {
        Console.SetOut(Output);
    }
}

All set. Let’s write our first test case.

Test case 1 - File upload success

If you remember, very beginning of this post we made a change in UploadFileToAzBlob method. We removed the dependency on IServiceProvider and added dependency on IAzBlobService.

So, we will create a mock object of IAzBlobService and pass it to UploadFileToAzBlob method.

UnitTests.cs
1
2
3
4
5
6
7
8
9
10
11
[Fact]
public async Task File_Upload_Success()
{
    var azBlobService = new Mock<IAzBlobService>();
    azBlobService
                .Setup(x => x.UploadFileToAzBlobAsync(It.IsAny<string>()))
                .ReturnsAsync(true);
    
    await Program.UploadFileToAzBlobAsync(azBlobService.Object);
    Assert.Contains("File uploaded successfully", Output.ToString());
}

At line 4, we are creating a mock object of IAzBlobService.

At line 5, we are setting up the mock object to return true when UploadFileToAzBlob method is called.

At line 9 we are calling UploadFileToAzBlob method and passing the mock object of IAzBlobService.

At line 10, we are asserting that the output contains the string File uploaded successfully.

At this stage, we have completely ignoring the implementation of UploadFileToAzBlob method. We are only testing the output of the method and not the implementation. This is the beauty of Moq. We can test the output of the method without worrying about the implementation.

Test case 2 - File upload failure

Similarly, we can write the test case for the scenario when the file upload fails.

UnitTests.cs
1
2
3
4
5
6
7
8
9
10
11
[Fact]
public async Task File_Upload_Failure ()
{
     var azBlobService = new Mock<IAzBlobService>();
     azBlobService
                .Setup(x => x.UploadFileToAzBlobAsync(It.IsAny<string>()))
                .ReturnsAsync(false);
    
    await Program.UploadFileToAzBlobAsync(azBlobService.Object);
    Assert.Contains("File upload failed", Output.ToString());
}

Here, the only difference is we are setting up the mock object to return false when UploadFileToAzBlobAsync method is called.

Outro

That’s it. We have written two test cases for the method UploadFileToAzBlobAsync. We have used Moq to mock the dependency IAzBlobService and xUnit to write the test cases.

However, we have not tested the implementation of UploadFileToAzBlobAsync method. We have only tested the output of the method.

We will write the test cases for the implementation of UploadFileToAzBlobAsync method in the next post, which will be act as integration test cases.

You can find the source code here.