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

时间:2020-04-01 08:59:36

标签: c# azure unit-testing azure-functions xunit

我有一个时间触发的Azure函数,我想使用XUnit和MOQ进行测试。

虽然我知道我需要使用类实例Run(其中

)来调用类的funTimeTriggeredObj方法
funTimeTriggered funTimeTriggeredObj = new funTimeTriggered(queueSchedulerMock.Object, telemetryHelperMock.Object)

喜欢

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

其中

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

我不确定如何模拟param1param2对象的TimerInfoExecutionContext

我问的原因是因为'TimerInfo'和'ExecutionContext'都没有实现任何可以被嘲笑的接口。

下面是我的实际功能实现。任何帮助将不胜感激。

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

5 个答案:

答案 0 :(得分:4)

如果使用这些类的实际实例没有任何不良影响,则可以实际初始化它们,然后创建实际实例并将它们传递给被测函数。

如果使用实际实例没有不良影响,则不必成为接口或模拟它们

//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

由于在此测试用例中,该功能甚至没有使用计时器,因此可以将其完全忽略

//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

答案 1 :(得分:3)

您可以将Azure函数的逻辑放在单独的类中,然后为该类编写单元测试。

如果使用另一个触发器(例如HTTP)创建了另一个函数,则可以做一个集成测试。

答案 2 :(得分:0)

设置正确。但是,您不必尝试模拟TimerInfo和ExecutionContext或实现它们,而可以直接发送null,因为您没有在函数中使用它们。

答案 3 :(得分:0)

我需要做类似的测试。我虽然使用了FakeItEasy,但是希望它能对您(或其他人)有所帮助。

使用的包裹:

<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" />

一些测试:

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

答案 4 :(得分:0)

 // 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);