涉及 Microsoft.Azure.ServiceBus.Core.MessageReceiver 时如何进行单元测试

Jua*_*uan 3 c# unit-testing azure-functions

我正在尝试对与 MessageReceiver 类具有依赖关系的 Azure 函数进行单元测试。类定义如下:

public class MessageReceiver : ClientEntity, IMessageReceiver, IReceiverClient, IClientEntity
Run Code Online (Sandbox Code Playgroud)

我需要moq的方法属于接口IMessageReceiver

我尝试模拟 MessageReceiver 类,并设置 CompleteAsync 方法,但出现错误:

Non-overridable members (here: MessageReceiver.CompleteAsync) may not be used in setup / verification expressions.
Run Code Online (Sandbox Code Playgroud)

据我所知,这意味着非虚拟、抽象或重写的方法不能被重写/最小起订量。我尝试手动创建一个子类并使用定义中的新关键工作声明一个方法 CompleteAsync,

public new Task CompleteAsync(string lockToken)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,代码会在调用 CompleteAsync 方法时引发错误:

Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
Run Code Online (Sandbox Code Playgroud)

但我认为它不起作用,因为我尝试按 F11 进入该方法,但我的方法没有被调用...有什么想法或建议吗?

小智 9

我的解决方案是:

  1. 在 Azure 函数类上创建公共属性。(消息接收者)

  2. 当引用为 null 时,在 Run 方法中设置此属性的值

  3. 在调用 Run 方法之前,使用模拟在单元测试中设置此属性的值。

     //Azure Function:
     public class ServiceReportingFunction
     {
         private ILogger<ServiceReportingFunction> Logger { get; }
    
         //Needed for unit testing until 'IMessageReceiver messageReceiver' is supported as argument in the Run Method.
         public IMessageReceiver MessageReceiver { get; set; } // Step 1
    
    
         public ServiceReportingFunction(IConfigurationSettings configurationSettings, IReportSubscriptionClient reportSubscriptionClient, IServiceTokenProviderGateway serviceTokenProviderGateway, ILogger<ServiceReportingFunction> logger)
         {
             Logger = logger;
         }
    
         [FunctionName("ServiceReportingFunction")]
         public async Task Run([ServiceBusTrigger("%ServiceReportingQueueName%", Connection = "ServiceBusConnectionString")]Message message, MessageReceiver messageReceiver)
         {
             // Step 2
             if (MessageReceiver == null)
                 MessageReceiver = messageReceiver;
    
             ...
         }
     }
    
     //Unit (Xunit) test:
     public class ServiceReportingFunctionTests
     {
         [Fact]
         public async void Test_ServiceReportingFunction()
         {
             var logger = A.Fake<ILogger<ServiceReportingFunction>>();
             var messageReceiver = A.Fake<IMessageReceiver>(); // Step 3
    
             var function = new ServiceReportingFunction(logger);
             function.MessageReceiver = messageReceiver;
    
             ....
    
             await function.Run(message, null);
    
            ....
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)


dev*_*ull 5

正如您所发现的,Moq 要求成员可以通过接口或修饰符进行覆盖(有关更多信息,virtual请参阅此答案)。因此,理想情况下,在这种情况下,您将注入IMessageReceiver接口并能够正常模拟它。但是,目前您似乎无法IMessageReceiver在 Azure Functions 中注入接口(请参阅GitHub 功能请求)。


作为解决方法,您可以为接受IMessageReceiver. internal该包装器可以像函数类中的“内部”方法一样简单(内部如未装饰为 Azure Function 触发器,不一定使用访问修饰符)。例如,如果您当前的方法如下所示:

[FunctionName("Foo")]
public Task RunAsync(
    [ServiceBusTrigger] Message serviceBusMessage,
    MessageReceiver messageReceiver)
{
    // implementation
}
Run Code Online (Sandbox Code Playgroud)

您可以添加一个单独的、可测试的方法,该方法RunAsync将传递到:

[FunctionName("Foo")]
public Task RunAsync(
    [ServiceBusTrigger] Message serviceBusMessage,
    MessageReceiver messageReceiver)
{
    return TestableRunAsync(serviceBusMessage, messageReceiver);
}

public Task TestableRunAsync(
    [ServiceBusTrigger] Message serviceBusMessage,
    IMessageReceiver messageReceiver)
{
    // implementation
}
Run Code Online (Sandbox Code Playgroud)

然后在单元测试中,您可以TestableRunAsync使用 的 Mock 实例进行调用IMessageReceiver