How to write unit tests for Azure Functions that use wrapper? - c#

I use a wrapper class on all of my Azure Functions:
public interface IFunctionWrapper
{
Task<IActionResult> Execute(HttpRequest req, ExecutionContext context, Func<Task<IActionResult>> azureFunction);
}
public class FunctionWrapper : IFunctionWrapper
{
private readonly ILogger _log;
public FunctionWrapper(ILogger<FunctionWrapper> log)
{
_log = log;
}
public async Task<IActionResult> Execute(HttpRequest req, ExecutionContext context, Func<Task<IActionResult>> azureFunction)
{
try
{
// Log few extra information to Application Insights
// Do authentication
return await azureFunction();
}
catch (Exception ex)
{
// Return a custom error response
}
}
}
And here is how it is used in a function:
public class MyFunctions
{
private readonly IFunctionWrapper _functionWrapper;
public MyFunctions(IFunctionWrapper functionWrapper)
{
_functionWrapper = functionWrapper;
}
public async Task<IActionResult> GetPost(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ExecutionContext context,
ILogger log)
{
return await _functionWrapper.Execute(req, context, async () =>
{
// Function code...
return new JsonResult(post);
});
}
}
I am trying to write unit tests for this GetPost function. How can I mock the FunctionWrapper class in this situation?

Mock the desired behavior of the wrapper abstraction.
The following example uses MOQ to mock the wrapper. Pay attention to the setup of the mock
[TestClass]
public class MyFunctionsTests {
[TestMethod]
public async Task GetPost_Should_Execute_Wrapper() {
//Arrange
//mock the wrapper
IFunctionWrapper wrapper = Mock.Of<IFunctionWrapper>();
//configure the mocked wrapper to behave as expected when invoked
Mock.Get(wrapper)
.Setup(_ => _.Execute(It.IsAny<HttpRequest>(), It.IsAny<ExecutionContext>(), It.IsAny<Func<Task<IActionResult>>>()))
.Returns((HttpRequest r, ExecutionContext c, Func<Task<IActionResult>> azureFunction) =>
azureFunction()); //<-- invokes the delegate and returns its result
MyFunctions function = new MyFunctions(wrapper);
//these should be initialized as needed for the test
HttpRequest req = null;
ExecutionContext ctx = null;
ILogger log = Mock.Of<ILogger>();
//Act
IActionResult result = await function.GetPost(req, ctx, log);
//Assert
result.Should().NotBeNull();
//verify that mocked wrapper was called
Mock.Get(wrapper).Verify(_ => _.Execute(It.IsAny<HttpRequest>(), It.IsAny<ExecutionContext>(), It.IsAny<Func<Task<IActionResult>>>()));
//...perform other assertions here
}
}
The code in the original question omitted most of the body of the subject under test. That being said, this example is based on what was originally provided, which was used to create a reproducible example used to create the test above

You don't need to create a wrapper interface at all:
HttpRequest is mockable: https://mahmutcanga.com/2019/12/13/unit-testing-httprequest-in-c/
ExecutionContext can be mocked (or as its just a POCO used as-is)
ILogger can be mocked
Use dependency injection to inject the dependencies of the function (and mock those then).
Remember you really only want to test that parameter validation and possible parsing works correctly.

Related

Unit Testing Azure Functions With Dependency Injection

I haven't written any Azure functions in quite a long time, and thought I'd refresh myself today, but I've clearly forgotten how to write appropriate unit tests for them. I have the following Function - it picks a random quote from a list;
public class QuoteFunction
{
private readonly IQuoteBank _repository;
public QuoteFunction(IQuoteBank repository)
{
_repository = repository;
}
[FunctionName("GetQuote")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
Quote quote = _repository.getQuote();
return new OkObjectResult(quote);
}
}
and it uses dependency injection to obtain the list of quotes - I have the following in Startup;
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IQuoteBank, QuoteBank>();
builder.Services.AddLogging();
}
which is injected into the constructor of the Function. as shown in the first snippet.
What I am struggling with is how I can use Moq to force the quote (which is randomly selected) to be consistent. I know I can mock the Interface IQuoteBank - but there is no where I can pass this mock object into the Run method.
So what I want to know is how I can pass a mock object to make the same quote be produced for unit testing? Has anyone done anything like this before? any examples in github?
I'm pretty sure I did a few years ago, just cant remember at all.
Setup the mock and pass that into the subject under test via constructor injection.
public async Task MyTestMehod() {
// Arrange
Mock<IQuoteBank> mock = new Mock<IQuoteBank>();
mock.Setup(_ => _.getQuote()).Returns("my consistent quote here")
var subject = new QuoteFunction(mock.Object);
//Act
IActionResult result = await subject.Run(Mock.Of<HttpRequest>(), Mock.Of<ILogger>());
//Assert
// ... assert my expected behavior
}

How can I write unit test for my background service?

I'm working with the HostBuilder in .NET Core (not the WebHost !).
I have one Hosted Service running in my application that overrides the ExecuteAsync/StopAsync methods of the background Service and I want to unit test it.
Here is my HostedService:
public class DeviceToCloudMessageHostedService : BackgroundService
{
private readonly IDeviceToCloudMessageService _deviceToCloudMessageService;
private readonly AppConfig _appConfig;
public DeviceToCloudMessageHostedService(IDeviceToCloudMessageService deviceToCloudMessageService, IOptionsMonitor<AppConfig> appConfig)
{
_deviceToCloudMessageService = deviceToCloudMessageService;
_appConfig = appConfig.CurrentValue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _deviceToCloudMessageService.DoStuff(stoppingToken);
await Task.Delay(_appConfig.Parameter1, stoppingToken);
}
}
public override Task StopAsync(CancellationToken cancellationToken)
{
Log.Information("Task Cancelled");
_deviceToCloudMessageService.EndStuff();
return base.StopAsync(cancellationToken);
}
I already found this post: Integration Test for Hosted Service in .NET Core
But it's explained for a QueuedBackgroundService and I don't really know if I can test mine the same way.
I just want to know if my code is executed. I don't want any specific result.
Do you have any idea of how I can test it?
You should still be able to follow a similar format as the linked answer.
Mock the dependencies and inject them, invoke the methods under test and assert the expected behavior.
The following uses Moq to mock the dependencies along with ServiceCollection to do the heavy lifting of injecting the dependencies.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestMethod]
public async Task DeviceToCloudMessageHostedService_Should_DoStuff() {
//Arrange
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IHostedService, DeviceToCloudMessageHostedService>();
//mock the dependencies for injection
services.AddSingleton(Mock.Of<IDeviceToCloudMessageService>(_ =>
_.DoStuff(It.IsAny<CancellationToken>()) == Task.CompletedTask
));
services.AddSingleton(Mock.Of<IOptionsMonitor<AppConfig>>(_ =>
_.CurrentValue == Mock.Of<AppConfig>(c =>
c.Parameter1 == TimeSpan.FromMilliseconds(1000)
)
));
var serviceProvider = services.BuildServiceProvider();
var hostedService = serviceProvider.GetService<IHostedService>();
//Act
await hostedService.StartAsync(CancellationToken.None);
await Task.Delay(1000);//Give some time to invoke the methods under test
await hostedService.StopAsync(CancellationToken.None);
//Assert
var deviceToCloudMessageService = serviceProvider
.GetRequiredService<IDeviceToCloudMessageService>();
//extracting mock to do verifications
var mock = Mock.Get(deviceToCloudMessageService);
//assert expected behavior
mock.Verify(_ => _.DoStuff(It.IsAny<CancellationToken>()), Times.AtLeastOnce);
mock.Verify(_ => _.EndStuff(), Times.AtLeastOnce());
}
Now, ideally this would count as testing framework code since you are basically testing that a BackgroundService behaves as expected when run, but it should demonstrate enough about how one would test such a service in isolation
Another example based on #Nkosi's excellent answer. For I was testing this StartupBackgroundService, which has a protected method ExecuteAsync:
public class StartupBackgroundService : BackgroundService
{
private readonly StartupHealthCheck _healthCheck;
public StartupBackgroundService(StartupHealthCheck healthCheck)
=> _healthCheck = healthCheck;
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_healthCheck.StartupCompleted = true;
return Task.CompletedTask;
}
}
I can't change the scope from protected to internal and expose it with [assembly: InternalsVisibleTo("TestsAssembly")] because its derived from an Abstract class.
So I came up with this magic, it calls ExecuteAsync not StartAsync:
[Test]
public async Task Should_Setup_StartupBackgroundService()
{
//Arrange
var startUpBackServ = new StartupBackgroundService(new Base.HealthCheck.StartupHealthCheck());
// Act
startUpBackServ.StartAsync(It.IsAny<CancellationToken>()); // It calls ExecuteAsync magically!
//Assert
}
It's MAGIC!
Here's the StartupHealthCheck:
public class StartupHealthCheck : IHealthCheck
{
public bool StartupCompleted { get; set; }
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context = null, CancellationToken cancellationToken = default)
{
if (StartupCompleted)
{
return Task.FromResult(HealthCheckResult.Healthy("The startup task has completed."));
}
return Task.FromResult(HealthCheckResult.Unhealthy("That startup task is still running."));
}
}

AzureFunctions.Autofac threadsafe dependency injection issue

I am using AzureFunctions.Autofac to inject into my Azure Functions web api. An example of the config:
public class DIConfig
{
public DIConfig()
{
DependencyInjection.Initialize(builder =>
{
// DAL
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().As<ICartContext>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
// Services
builder.RegisterType<InventoryServices>().As<IInventoryServices>().InstancePerLifetimeScope();
// Controllers ported from ASP.NET MVC Web API
builder.RegisterType<InventoryController>().InstancePerLifetimeScope();
});
}
Then my Azure functions, I have one class that defines all methods in the API
[DependencyInjectionConfig(typeof(DIConfig))]
public class InventoryFunctions : FunctionsApi
{
[FunctionName("GetProductsByCategory")]
// /inventory/categories/{id}/products
public static async Task<HttpResponseMessage> GetProductsByCategory(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory/categories/{id}/products")]
HttpRequestMessage req,
TraceWriter log,
int id,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.GetProductsByCategory(id);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("GetInventoryBySku")]
// /inventory/skus?sku=ASDF&sku=ASDG&sku=ASDH
public static async Task<HttpResponseMessage> GetInventoryBySku(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.QueryInventoryBySkuList(skuList);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("UpdateProductsQuantity")]
// /inventory
// Post
public static async Task<HttpResponseMessage> UpdateProductsQuantity(
[HttpTrigger(AuthorizationLevel.Function, "put", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var inventoryProducts = await req.Content.ReadAsAsync<List<InvProductOperation>>();
var result = await controller.UpdateAvailableProductsQuantity(inventoryProducts);
return JsonResponse(result, HttpStatusCode.OK);
}
But I keep getting this error:
A second operation started on this context before a previous
asynchronous operation completed. Use 'await' to ensure that
any asynchronous operations have completed before calling
another method on this context. Any instance members are not
guaranteed to be thread safe.
I have verified that async and await are used properly, so following the error message's recommendation isn't fixing it. What appears to be the issue is that IDbContext is not honoring the InstancePerLifetimeScope as expected. Is this happening because I have more than one method in my InventoryFunctions class? Or is AzureFunctions.Autofac not threadsafe?
Change the registration of the DbContext to this:
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
You can find a deeper explanation of mine for why this is happening here.
I was going by this SO answer: Autofac - InstancePerHttpRequest vs InstancePerLifetimeScope which said that InstancePerLifetimeScope was the non-ASP.NET equivalent of InstancePerRequest.
I spoke to the developers and they said the truth is that getting one DbContext per HttpRequest was the default behavior when you simply register using builder.RegisterType<SecretCompanyContext>.As<IDbContext>() so there's some misinformation out there.
So the solution is, instead of using
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
or
builder.RegisterType<SecretCompanyContext>().As<IDbContext>().InstancePerLifetimeScope();
one should just use
builder.RegisterType<SecretCompanyContext>().As<IDbContext>();
if the goal is one instance per HTTP request.

How to mock custom ILoggerManager to do UnitTest

I declared a class LogManager and an interface ILogManager, and has a method LoggerError, this method use ILogger (DI) to logerror and do other things.
There is a class A (DI ILogClass ) and declare a method funA(), and funA() will use _LogManager.LoggerError("test message").
When do I unit test with mock ?
How to Verify _LogManager.LoggerError("test message") ?
If I just use ILogger in class A, I can Verify with :
loggerMock.Verify(l => l.Log(LogLevel.Error,It.IsAny<EventId>(),
It.Is<object>(o => o.ToString().Contains("test message")),null,It.IsAny<Func<object, Exception, string>>()), Times.Once);
But not support with ILogManager mock.
public class LoggerManager<TCategoryName> : ILoggerManager<TCategoryName>
{
private readonly ILogger<TCategoryName> _logger;
public LoggerManager(ILogger<TCategoryName> logger)
{
this._logger = logger;
}
public void LogError(string message, LoggerErrorType type)
{
var errorLogger = NLog.LogManager.GetLogger(loggerName);
errorLogger.Error(message);
}
}
class A
{
....DI
public void Test()
{
_loggerManager.LogError("test message")
}
}
UT:
public void TestUT()
{
var loggerMock = new Mock ILoggerManager A>>();
var service = ServiceFactory.Create<A>(loggerMock);
service.Test();
//how to Verify logerror message?
}
Your LoggerManager<TCategoryName> is just a wrapper over the ILogger<TCategoryName> and thus you can just mock ILogger<TCategoryName> and verify the same as you have already done and that should work just fine.
Though while making the actuall call to SUT you will have to dependency inject the LoggerManager<TCategoryName> accordingly

Mock httpcontext.current.request.files

I am implementing nUnit Test case for one of my method called, UploadFile(), some thing like below
public void UploadFile(string siteId, string sitePageId)
{
int fileCount = HttpContext.Current.Request.Files.Count;
//Rest of code
}
so basically i am reading file using HttpContext.Current.Request.Files.
From UI it is working fine but when i am implementing nUnit test case for it, i am not able to mock HttpContext.Current.Request.Files. I googled about some of mocking tools but there also i didn't get anything related to mocking of HttpContext.Current.Request.Files. Please help me how to mock it or write test case for my method.
You could use dependency injection and then inject an instance of HttpContextBase into the class. Supposing you're using MVC:
public class MyController : Controller
{
HttpContextBase _context;
public MyController(HttpContextBase context)
{
_context = context
}
public void UploadFile(string siteId, string sitePageId)
{
int fileCount = _context.Request.Files.Count;
//Rest of code
}
}
Now you can instantiate the controller with a mock of HttpContextBase. This is how you would do it with Moq:
[Test]
public void File_upload_test()
{
var contextmock = new Mock<HttpContextBase>();
// Set up the mock here
var mycontroller = new MyController(contextmock.Object);
// test here
}

Categories