【发布时间】:2018-01-11 20:59:18
【问题描述】:
到目前为止,我已经能够为 Azure Functions 设置单元测试并且效果很好。但是,对于我当前的项目,我需要使用动态或命令式绑定。 https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#imperative-bindings
这导致我的单元测试出现我似乎无法解决的问题。
我的函数如下所示:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.ServiceBus.Messaging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace My.Functions
{
public static class MyFunc
{
[FunctionName("my-func")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req,
Binder binder)
{
dynamic data = await req.Content.ReadAsAsync<object>();
byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
MemoryStream stream = new MemoryStream(bytes, writable: false);
var sbMsg = new BrokeredMessage(stream) { ContentType = "application/json" };
var attributes = new Attribute[]
{
new ServiceBusAccountAttribute("some-sb-account"),
new ServiceBusAttribute("some-queue-or-topic", AccessRights.Send)
};
var outputSbMessage = await binder.BindAsync<IAsyncCollector<BrokeredMessage>>(attributes);
await outputSbMessage.AddAsync(sbMsg);
return req.CreateResponse(HttpStatusCode.OK, "OK");
}
}
}
在函数代码接近尾声时,我将这个绑定器配置为保存一个 BrokeredMessages 列表。这是通过调用活页夹上的 BindAsync 来完成的。
属性是动态设置的,包含服务总线连接和主题名称。当部署到 Azure 时,这一切都很好,所以功能方面一切都很好。 到目前为止一切顺利。
但是,我正在努力让我的测试运行。为了能够调用该函数,我需要提供参数。 HttpTrigger 这很常见,但是对于 Binder 我不知道提供什么。
为了测试,我使用这种方法:
[TestMethod]
public void SendHttpReq()
{
// Setup
var httpRequest = GetHttpRequestFromTestFile("HttpRequest");
var sbOutput = new CustomBinder();
// Act
var response = SendToServicebus.Run(httpRequest, sbOutput);
// Assert
Assert.AreEqual(sbOutput.Count(), 1);
// Clean up
}
我使用从 Binder 继承的 CustomBinder,因为在 'BindAsync' 上的函数中只有一个 Binder 实例失败,抛出“对象引用未设置为对象的实例”。看来 binder 的构造函数实际上并没有被调用的意思。
在 CustomBinder 中,我重写 BindAsync 以返回 BrokeredMessages 的通用列表。
public class CustomBinder : Binder
{
public override async Task<TValue> BindAsync<TValue>(Attribute[] attributes, CancellationToken cancellationToken = new CancellationToken())
{
return (TValue)((object)(new List<BrokeredMessage>()));
}
}
投掷失败也不足为奇:
InvalidCastException:无法将“System.Collections.Generic.List'1[Microsoft.ServiceBus.Messaging.BrokeredMessage]”类型的对象转换为“Microsoft.Azure.WebJobs.IAsyncCollector`1[Microsoft.ServiceBus.Messaging. BrokeredMessage]'。
我找不到 IAsyncCollector 的实现,所以也许我需要以不同的方式处理这个问题?
我的实际目标是能够验证代理消息列表,因为该函数将输出到 Azure 服务总线。
【问题讨论】:
-
有
IBinder接口,但比BinderAFAIK更受限制。有你需要的方法吗?为什么不也模拟IAsyncCollector? -
我尝试了 IBinder,但它不包含提供属性数组的方法,而这正是我所需要的。
-
我试图实现的目标是能够读取从函数返回的代理消息列表。我想在运行函数后验证消息内容是否符合预期。
-
这就是为什么你可以模拟
IAsyncCollector。不过,验证BrokeredMessage可能具有挑战性,甚至不确定您是否可以从中读取消息内容。 -
在非绑定方案中,您在签名中使用 ICollector
,然后很容易对此进行测试。对于 Binder 来说,这确实是一个挑战。
标签: c# unit-testing azure-functions