简体   繁体   English

对时间触发的 Azure 函数进行单元测试

[英]Unit Test a Time Triggered Azure Function

I've got a time-triggered Azure Function which I want to test with XUnit and MOQ.我有一个时间触发的 Azure 函数,我想用 XUnit 和 MOQ 测试它。

While I know I need to call the Run method of the class using an instance of the class say funTimeTriggeredObj where虽然我知道我需要使用类的实例调用该类的Run方法,但说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.我不确定我应该如何模拟分别是TimerInfoExecutionContext对象的param1param2

The reason why I'm asking is because neither 'TimerInfo' nor 'ExecutionContext' implements any interface which can be mocked.我问的原因是因为“TimerInfo”和“ExecutionContext”都没有实现任何可以模拟的接口。

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;
        }
    }
}

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

You could put the logic of your Azure function into a separate class and write unit tests for that class.您可以将 Azure 函数的逻辑放在一个单独的类中,并为该类编写单元测试。

What could be done is an integration test, if there was a another function created with a different trigger (for example HTTP), doing the same thing.可以做的是集成测试,如果使用不同的触发器(例如 HTTP)创建了另一个函数,做同样的事情。

The setup is right.设置是对的。 But instead of trying to mock TimerInfo and ExecutionContext, or implementing them, you can simply send null since you don't use them inside your function.但是,与其尝试模拟 TimerInfo 和 ExecutionContext 或实现它们,不如简单地发送 null,因为您不在函数中使用它们。

I had similar tests that I needed to do.我有我需要做的类似测试。 I used FakeItEasy though, but I'm hoping this will help you (or others) nonetheless.虽然我使用了 FakeItEasy,但我希望这会帮助你(或其他人)。

Packages used:使用的软件包:

<PackageReference Include="FakeItEasy" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />

Some tests:一些测试:

public class FunTimeTriggeredConstructorTests
{
    private ITelemetryHelper _fakeITelemetryHelper;
    private IQueueScheduler _fakeIQueueScheduler;

    public FunTimeTriggeredConstructorTests()
    {
        _fakeITelemetryHelper= A.Fake<ITelemetryHelper >();
        _fakeIQueueScheduler = A.Fake<IQueueScheduler>();
    }

    [Fact]
    public void ShouldThrow_ArgumentNullException_When_IQueueScheduler_Is_Null()
    {
        _fakeIQueueScheduler = null;

        Assert.Throws<ArgumentNullException>(() => new FunTimeTriggered(_fakeITelemetryHelper, _fakeIQueueScheduler));
    }
}

public class RunTests
{
    private ITelemetryHelper _fakeITelemetryHelper;
    private IQueueScheduler _fakeIQueueScheduler;
    private TimerInfo _fakeTimerInfo;
    private ExecutionContext _fakeExecutionContext;
    private ILogger _fakeILogger;
    private FunTimeTriggered _funTimeTriggered;

    public RunTests()
    {
        _fakeITelemetryHelper= A.Fake<ITelemetryHelper>();
        _fakeTimerInfo = A.Fake<TimerInfo>();
        _fakeIQueueScheduler = A.Fake<IQueueScheduler>();
        _fakeExecutionContext = A.Fake<ExecutionContext>();
        _fakeILogger = A.Fake<ILogger>();

        _funTimeTriggered = new FunTimeTriggered(_fakeITelemetryHelper, _fakeIQueueScheduler);
    }

    [Fact]
    public async Task ShouldCall_IQueueScheduler_SendEventsToServiceBusAndDeleteFromSQS_AtMost_Once()
    {
        A.CallTo(() => _fakeExecutionContext.InvocationId).Returns("");

        await FunTimeTriggered.Run(_fakeTimerInfo, _fakeExecutionContext, _fakeILogger);

        A.CallTo(() => _queueScheduler.SendEventsToServiceBusAndDeleteFromSQS()).MustHaveHappenedOnceExactly();
    }
}
 // Arrange
 TimerSchedule schedule = new DailySchedule("2:00:00");
 TimerInfo timerInfo = new TimerInfo(schedule, It.IsAny<ScheduleStatus>(), false);
    
 // Act
 await _functions.TimerTrigerFunction(timerInfo, _durableOrchestrationClient.Object, _log.Object);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM