I want to test an Azure Function app (.NET 8) using Moq.
The solution consists of two projects:
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?
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
, andILogger
, without including relevant package dependencies ofAssetGovernance
toAssetGovernance.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.