Search code examples
c#testingazure-functionsmoq

How to mock library dependencies in an Azure Function app without adding the libraries as dependencies of the test project?


I want to test an Azure Function app (.NET 8) using Moq.

The solution consists of two projects:

  • AssetGovernace (source)
  • AssetGovernace.Tests (tests)

While I can easily mock out IApplicationService, IApplicationNotificationRepository, and IQueueService, like:

var _applicationServiceMock = new Mock<IApplicationService>();

I don't know how to mock out IMapper (AutoMapper), IConfiguration, and ILogger, without including relevant package dependencies of AssetGovernance to AssetGovernance.Tests for making the interfaces available. Is that the right approach?

When looking into this sample repository, I don't see the test project addressing my issues.

https://github.com/Azure-Samples/azure-functions-code-testing-sample/tree/main

How should I mock out these framework and library dependencies in an Azure Function app?

ApplicationGovernanceService class:

public class ApplicationGovernanceService(
    IApplicationService applicationService,
    IApplicationNotificationRepository applicationNotificationRepository,
    IQueueService queueService,
    IMapper mapper,
    IConfiguration configuration,
    ILogger<ApplicationGovernanceService> logger)
    : IApplicationGovernanceService

IApplicationGovernanceService interface:

public interface IApplicationGovernanceService
{
    public Task CheckApplicationOwnership(CancellationToken cancellationToken = default);
}

Bonus: Is there a way to have dependency injection in the test project?


Solution

  • You don't write that you're using test-driven development (TDD), so I'm assuming that you don't, but I'll bring it into the discussion anyway, because it's relevant to this discussion, too.

    An often-mentioned benefit of TDD is that it makes design problems visible earlier than you would otherwise have noticed them. This would have been the case here, too. The tests only highlight a problem.

    I don't know how to mock out IMapper (AutoMapper), IConfiguration, and ILogger, without including relevant package dependencies of AssetGovernance to AssetGovernance.Tests for making the interfaces available.

    This is, in a nutshell, the problem. If an interface ships with its implementation, it violates the Dependency Inversion Principle (DIP) because in order to depend on the abstraction, you must also depend on the implementation detail.

    This is the problem you're running into here. The interfaces are defined together with their implementations. If you want to follow the DIP, you should, at best, ignore all interfaces defined by dependencies. Instead, you could define your own. According to the DIP,

    "clients [...] own the abstract interfaces"

    - APPP, Robert C. Martin, chapter 11

    In other words, client code, in this case AssetGovernace, should define the interfaces it needs. When it, instead, pulls in third-party interfaces, it breaks the DIP.

    You could argue that using those third-party interfaces is 'pragmatic', and you're allowed to do that. After all, no law says that you should follow the SOLID principles.

    If you chose to do so, however, you'll also need to add those dependencies to the test code if you want to use Moq to replace them with Test Doubles.