Dependency Injection is one of the most elegant tool to inject dependencies at runtime, which makes the application more loosely-coupled. It also helps in building modularized code that improves testability by enabling unit-testing on modules.

In this post, I will be creating a complicated dependency setup to explain the idea of how dependencies can be injected using an Azure Function. Then, I will mock these dependencies to test the setup using Moq and xUnit. This post will help you to understand how dependency injection setup can be seamlessly simulated using these amazing frameworks.

You can use the below GitHub Repo for referring the complete code mentioned here.

azure-dev-projects/MoqXunitWithDI

First, let’s understand the modularized application which we will be building.

Table of contents

Building Custom Application with Dependencies

We need to build a complicated setup of dependencies to explain the usage of dependency injection. In order to do this, we will be implementing the below shown setup.

Untitled.png

We will be creating a FooModule class library to get all the required classes in one place as shown below.

Untitled%201.png

We will first start with our interfaces.

  • IFooCollection → This interface will help in providing all the FooRegistration objects

      public interface IFooCollection
      {
          public IEnumerable<FooRegistration> GetAllRegistrations();
      }
    
  • IFooWork → This interface will help in providing FooResult object after doing some simulated checking

      public interface IFooWork
      {
          Task<FooResult> CheckFooAsync(string param1, string param2, FooContext context, FooToken fooToken);
      }
    
  • IFooLogicImplementer → This interface will help in designing our FooLogicImplementer that will give FinalResult after executing some logic

      public interface IFooLogicImplementer
      {
          Task<FinalResult> ExecuteLogic();
      }
    

These interfaces are the dependencies using which we will implement our FooLogicImplementer as shown below.

//FooLogicImplementer.cs
public class FooLogicImplementer: IFooLogicImplementer
{
    private readonly List<FooRegistration> _foos = new List<FooRegistration>();

    public FooLogicImplementer(IEnumerable<IFooCollection> data)
    {
        foreach(var foo in data)
        {
            _foos.AddRange(foo.GetAllRegistrations());
        }
    }
    // simulate logic
    // We need to test this functionality using unit test
    public async Task<FinalResult> ExecuteLogic()
    {
        List<FooResult> data = new List<FooResult>();
        foreach(var _foo in _foos)
        {
            data.Add(await _foo.Exec());
        }
        var isAllGood = true;
        foreach(var result in data)
        {
            if(result.Val == FooResultVals.BadResult)
            {
                isAllGood = false;
            }
        }
        if (isAllGood)
        {
            return new FinalResult
            {
                FinalRes = "Good",
                Data = data
            };
        }
        else
        {
            return new FinalResult
            {
                FinalRes = "Bad",
                Data = data
            };
        }
    }
}

The above implementation is following below process:

  1. Collection of IFooCollection will be injected using dependency injection.
  2. Fire GetAllRegistrations of each IFooCollection to get FooRegistration objects.
  3. For each of the FooRegistration objects, call exec function and collect FooResult objects.
  4. If all the results are Good, then set final result to be Good
  5. Otherwise, if any of the result is Bad, the set final result to be Bad

As you can see, this implementation is dependent on several interfaces to derive the final result to be good or bad. Hence, in order to test this implementation, we would need to mock these dependencies.

You can view/download the entire module using below GitHub URL:

azure-dev-projects/MoqXunitWithDI/FooModule

Now, we are ready to test this module. We will first test it using Azure Functions to see if our dependencies can be injected efficiently and then use unit testing.

Testing Custom Application with Dependencies

Our objective is to test the FooLogicImplementer class and its ExecuteLogic functionality by verifying FinalResult.

Test using Azure Function

We can test whether our dependencies are working by creating an Azure function application. The project can be viewed using the below GitHub URL:

azure-dev-projects/MoqXunitWithDI/FooLogicFnApp

Untitled%202.png

We will inject the above dependencies during startup using our custom implementations as shown below.

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // inject dependencies
        builder.Services.AddScoped<IFooCollection, FooLogic1>(sp =>
        {
            return new FooLogic1(new FooWork1());
        });
        builder.Services.AddScoped<IFooCollection, FooLogic2>(sp =>
        {
            return new FooLogic2(new FooWork2());
        });

        // inject implementer
        builder.Services.AddScoped<IFooLogicImplementer, FooLogicImplementer>();
    }
}

Here, FooLogic1 derives from IFooCollection and implements GetAllRegistrations function to generate List<FooRegistration> as shown below.

public class FooLogic1 : IFooCollection
{
    private readonly IFooWork _fooWork;

    public FooLogic1(IFooWork fooWork)
    {
        _fooWork = fooWork;
    }

    public IEnumerable<FooRegistration> GetAllRegistrations()
    {
        return new List<FooRegistration>
        {
            new FooRegistration("logic_reg_1_1", _fooWork, new FooToken(), new List<string> {"tag1", "tag2"}),
            new FooRegistration("logic_reg_1_2", _fooWork, new FooToken(), new List<string> {"tag1", "tag2"})
        };
    }
}

Similarly, FooLogic2 can be implemented to generate List<FooRegistration>.

Now, each FooRegistration takes IFooWork instance which needs to be provided. This instance sets whether it is either a good result or bad result as shown below for FooWork1. Here, it is setting a GoodResult.

public class FooWork1 : IFooWork
{
    public Task<FooResult> CheckFooAsync(string param1, string param2, FooContext context, FooToken fooToken)
    {
        return Task.FromResult(new FooResult(param1, param2, FooResultVals.GoodResult));
    }
}

Similarly, we can create FooWork2 that also derives from IFooWork and return a FooResult with a BadResult as shown below.

public class FooWork2 : IFooWork
{
    public Task<FooResult> CheckFooAsync(string param1, string param2, FooContext context, FooToken fooToken)
    {
        return Task.FromResult(new FooResult(param1, param2, FooResultVals.BadResult));
    }
}

We can now start and test our function app by hitting the endpoint using postman or any other REST client. Since one of our result returned by our IFooWork is good and one is bad, we should get the final result as bad result as shown below.

Untitled%203.png

This shows that our dependencies are being injected correctly and the logic of FooLogicImplementer is working as required. But, we need to test it for other scenarios as well, such as,

  • If all IFooWork are good result, then final result should be good.
  • If some IFooWork are good result and some are bad, then final result should be bad.
  • If all IFooWork are bad result, then final result should be bad.
  • If 1 IFooCollection is registered, then 1 is checked properly.
  • If 2 IFooCollection are registered, then 2 are checked properly.

Looking at these scenarios, I hope you can clearly see the problem with Azure function based testing. We need to change our code every time - create the required implementation for the scenario, run the application, hit it via a REST client and then view the results. Moreover, this approach is unlikely to scale well when we’ll have 10-20 scenarios.

So, a better way is to do unit-testing. We can create unit tests for each of the above scenarios, and then run them in isolation to verify the functionality. But, how do you inject these dependencies to test our FooLogicImplementer class? That’s where Moq library will enter!

Test using xUnit and Moq

First, we will create a unit test C# project (.NET core 3.1 LTS) and install following nuget packages:

  • Moq (4.16.1)
  • xunit (2.4.1)
  • xunit.runner.visualstudio (2.4.3)

You can view the entire test project using the below GitHub URL:

azure-dev-projects/MoqXunitWithDI/UnitTestLogicImplementer

Now, if you are wondering why xUnit has gained more popularity than MSTest - it is mostly because of the support of Theory attribute and its pluggable functionality in any project.

For more details, checkout this blog: The xUnit tool has gained popularity over MSTest

So, let’s try to create our 1 first unit test that will cover the scenario:

If all IFooWork are good result, then final result should be good.

In order to test the above scenario, we need to create an object of FooLogicImplementer class. But, if you look at the constructor, it needs IEnumerable<IFooCollection> data to instantiate. Hence, we will mock these dependencies so that we can create the required object.

  • Mock IFooWork to get a GoodResult.

      // setup work 1 to be good result
      _work1 = new Mock<IFooWork>();
      _work1.Setup(s => s.CheckFooAsync("logic_reg_1_1", It.IsAny<string>(), It.IsAny<FooContext>(), It.IsAny<FooToken>()))
          .Returns(Task.FromResult(new FooResult("logic_reg_1_1", It.IsAny<string>(), FooResultVals.GoodResult)));
    

    This setup will ensure that whenever mocked object of IFooWork is used to fire CheckFooAsync function with first parameter as "logic_reg_1_1" and other parameters could be any valid value, then it should result a FooResult that will have GoodResult. So, rather than implementing the logic, we have mocked the inputs and outputs as per our requirement.

  • Similarly, Mock another IFooWork to get a good result.
  • Create a FooRegistration collection which will take these mocked IFooWork objects.

      var data = new List<FooRegistration>()
      {
          new FooRegistration("logic_reg_1_1", _work1.Object, new FooToken(), new List<string> {"tag1", "tag2"}),
          new FooRegistration("logic_reg_1_2", _work2.Object, new FooToken(), new List<string> {"tag1", "tag2"})
      };
    
  • Mock the IFooCollection which will return the above collection when GetAllRegistrations is fired.

      _fooCollection.Setup(s => s.GetAllRegistrations()).Returns(data);
    
  • Using the above mocked dependencies, create an object of FooLogicImplementer and execute its logic function.

      // act
      var testImplementer = new FooLogicImplementer(new List<IFooCollection> { _fooCollection.Object });
    
      var res = await testImplementer.ExecuteLogic();
    
  • Assert the final result.

      Xunit.Assert.Equal("Good", res.FinalRes);
    

So, the final implementation of the scenario can be stated as follows:

public class FooLogicImplementerTests
{
    private readonly Mock<IFooCollection> _fooCollection;

    private Mock<IFooWork> _work1;
    private Mock<IFooWork> _work2;

    public FooLogicImplementerTests()
    {
        _fooCollection = new Mock<IFooCollection>();
    }

    [Fact]
    public async void If_All_Good_Return_Good()
    {
        // mock dependencies
        // setup work 1 to be good result
        _work1 = new Mock<IFooWork>();
        _work1.Setup(s => s.CheckFooAsync("logic_reg_1_1", It.IsAny<string>(), It.IsAny<FooContext>(), It.IsAny<FooToken>()))
            .Returns(Task.FromResult(new FooResult("logic_reg_1_1", It.IsAny<string>(), FooResultVals.GoodResult)));

        // setup work 2 to be good result
        _work2 = new Mock<IFooWork>();
        _work2.Setup(s => s.CheckFooAsync("logic_reg_1_2", It.IsAny<string>(), It.IsAny<FooContext>(), It.IsAny<FooToken>()))
            .Returns(Task.FromResult(new FooResult("logic_reg_1_2", It.IsAny<string>(), FooResultVals.GoodResult)));

        // Add some FooRegistrations
        var data = new List<FooRegistration>()
        {
            new FooRegistration("logic_reg_1_1", _work1.Object, new FooToken(), new List<string> {"tag1", "tag2"}),
            new FooRegistration("logic_reg_1_2", _work2.Object, new FooToken(), new List<string> {"tag1", "tag2"})
        };
        _fooCollection.Setup(s => s.GetAllRegistrations()).Returns(data);

        // act
        var testImplementer = new FooLogicImplementer(new List<IFooCollection> { _fooCollection.Object });

        var res = await testImplementer.ExecuteLogic();

        // assert
        Xunit.Assert.Equal("Good", res.FinalRes);
    }
}

Similarly, we can do mocking of these dependencies again and test for all of the above stated scenarios as shown below.

Untitled%204.png

The implementation of other test cases are very similar to the one showed here. You can review them using the below URL:

MoqXunitWithDI/UnitTestLogicImplementer/FooLogicImplementerTests.cs

This completes the idea of using Moq and xUnit with complicated dependency injection setup. As you can see, it can be really helpful to create unit tests for testing multiple scenarios, rather than updating the code again and again to cover all cases.

Cheers!

Leave a comment