【问题标题】:Unit Test a Time Triggered Azure Function单元测试时间触发的 Azure 函数
【发布时间】:2020-04-01 08:59:36
【问题描述】:

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

虽然我知道我需要使用类的实例调用该类的 Run 方法,例如 funTimeTriggeredObj where

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

【问题讨论】:

  • 这里有一些关于当你没有你试图模拟的类的所有权时该怎么做的讨论(因此在这种情况下不能根据需要将方法/属性标记为虚拟): stackoverflow.com/questions/20400734/…
  • 你真的需要模拟它们吗?它们似乎根本没有在您的代码中使用。我还认为它们都有没有副作用的公共构造函数,所以你甚至可以传入一个实际的实例。
  • Azure Function SDK 的 Azure Function Timer 触发器句柄,你不能在上面写单元测试。您应该为自己的代码编写单元测试
  • @PankajRawat 我已经为业务逻辑编写了一个单独的单元测试,我将它保存在不同的类中;这个要求是专门写一个“功能测试”
  • 您将很难创建TimerInfo 的新实例,因为它需要TimerSchedule 的实例,它是一个抽象类。但我只是通过伪装来避免这出戏。无论如何,您也应该伪造它,因为它并不是您真正测试的内容。您总是可以为您的方法可能从 TimerInfo 调用的某些方法伪造返回。

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


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    您可以将 Azure 函数的逻辑放入一个单独的类中,并为该类编写单元测试。

    可以做的是集成测试,如果使用不同的触发器(例如 HTTP)创建另一个函数,做同样的事情。

    【讨论】:

    • 我已经为业务逻辑编写了一个单独的单元测试,我将它保存在不同的类中;这个要求是专门写一个“功能测试”
    【解决方案3】:

    设置是正确的。但与其尝试模拟 TimerInfo 和 ExecutionContext 或实现它们,您可以简单地发送 null,因为您不在函数中使用它们。

    【讨论】:

      【解决方案4】:

      我需要做类似的测试。虽然我使用了 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();
          }
      }
      

      【讨论】:

        【解决方案5】:
         // 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);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-14
          • 2019-10-16
          • 1970-01-01
          • 1970-01-01
          • 2018-06-25
          相关资源
          最近更新 更多