Search code examples
c#azureunit-testingazure-functionsxunit

Unit Test a Time Triggered Azure Function


I've got a time-triggered Azure Function which I want to test with XUnit and MOQ.

While I know I need to call the Run method of the class using an instance of the class say funTimeTriggeredObj where

funTimeTriggered funTimeTriggeredObj = new funTimeTriggered(queueSchedulerMock.Object, telemetryHelperMock.Object)

like

funTimeTriggeredObj.Run(param1, param2, loggerMock.Object) 

where

private Mock<ILogger> loggerMock = new Mock<ILogger>() 

I'm not sure how should I mock the param1 & param2 which are TimerInfo and ExecutionContext objects respectively.

The reason why I'm asking is because neither 'TimerInfo' nor 'ExecutionContext' implements any interface which can be mocked.

Below is my actual function implementation. Any help whatsoever would be highly appreciated.

using System;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

public  class funTimeTriggered
{
    private  string  _invocationID;
    private readonly IQueueScheduler _queueScheduler;
    private readonly ITelemetryHelper _telemetryHelper;

    public funTimeTriggered(IQueueScheduler queueScheduler, ITelemetryHelper telemetryHelper)
    {
        _queueScheduler = queueScheduler;
        _telemetryHelper = telemetryHelper;
    }

    [FunctionName("funTimeTriggered")]
    public  async Task Run([TimerTrigger("0/10 * * * * *")]TimerInfo myTimer, ExecutionContext context, ILogger log)
    {
        log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
        try
        {
            _invocationID = context.InvocationId.ToString();
            await _queueScheduler.SendEventsToServiceBusAndDeleteFromSQS();
        }
        catch (Exception ex)
        {
            log.LogError(ex.Message);
            _telemetryHelper.LogException(ex);
            throw ex;
        }
    }
}

Solution

  • If there are no undesired effects of using actual instance of those classes and you can actually initialize them then create actual instance and pass them to the function under test.

    They do not have to be interfaces or mocked if using the actual instance(s) has no unwanted effects

    //Arrange
    
    //...omitted for brevity
    
    var param1 = new TimerInfo(...); 
    var param2 = = new ExecutionContext {
        InvocationId = Guid.NewGuid()
    };
    
    //Act
    await funTimeTriggeredObj.Run(param1, param2, loggerMock.Object);
    
    //Assert
    //...assert expected behavior
    

    And since in this test case the timer is not even used by the function, it can be ignored altogether

    //Arrange
    
    //...omitted for brevity
    
    var param1 = default(TimerInfo); //null
    var param2 = = new ExecutionContext {
        InvocationId = Guid.NewGuid()
    };
    
    //Act
    await funTimeTriggeredObj.Run(param1, param2, loggerMock.Object);
    
    //Assert
    //...assert expected behavior