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
我的解决方案是:
在 Azure 函数类上创建公共属性。(消息接收者)
当引用为 null 时,在 Run 方法中设置此属性的值
在调用 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)
正如您所发现的,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。