到目前为止,我已经能够为Azure Functions设置单元测试,并且效果很好。但是对于我当前的项目,我需要使用动态或命令式绑定。 https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#imperative-bindings
这导致我的单元测试问题似乎无法解决。
我的功能如下:
curl -x <myCompanyProxy>:<port> -k -O -L <link to file to download>
在函数代码的末尾,我将此绑定器配置为包含BrokeredMessages列表。这是通过在活页夹上调用BindAsync来完成的。
属性是动态设置的,包含servicebus连接和主题名称。当部署到Azure时,这一切都很有效,因此功能一切都很好。 到目前为止一切都很好。
然而,我正在努力让我的测试运行起来。为了能够调用该函数,我需要提供参数。 HttpTrigger这很常见,但对于Binder,我不知道该提供什么。
为了测试我使用这种方法:
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");
}
}
}
我使用从Binder继承的CustomBinder,因为在BindAsync&#39;的函数中只有一个Binder实例失败了。 throw&#39;对象引用未设置为对象的实例&#39;。看来绑定器的构造函数实际上并不是要调用它。
在CustomBinder中,我覆盖BindAsync以返回BrokeredMessages的通用列表。
[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
}
投掷失败也不足为奇:
InvalidCastException:无法转换类型&System; System.Collections.Generic.List&#39; 1 [Microsoft.ServiceBus.Messaging.BrokeredMessage]&#39;键入&#39; Microsoft.Azure.WebJobs.IAsyncCollector`1 [Microsoft.ServiceBus.Messaging.BrokeredMessage]&#39;。
我找不到IAsyncCollector的实现,所以也许我需要以不同的方式处理它?
我的实际目标是能够验证代理消息列表,因为该函数将输出到Azure servicebus。
答案 0 :(得分:0)
正如评论中所提到的,我同意嘲笑它是有道理的。您明确希望单元测试您自己的代码逻辑。只考虑您自己的业务逻辑,您可以假设实际的远程操作binder.BindAsync(...)
- 您无法控制 - 按预期工作。
在单元测试中模拟它应该使用这样的东西:
using FluentAssertions;
using Microsoft.Azure.WebJobs;
using Microsoft.ServiceBus.Messaging;
using Xunit;
[Fact]
public async Task AzureBindAsyncShouldRetrunBrokeredMessage()
{
// arrange
var attribute = new ServiceBusAccountAttribute("foo");
var mockedResult = new BrokeredMessage()
{
Label = "whatever"
};
var mock = new Mock<IBinder>();
mock.Setup(x => x.BindAsync<BrokeredMessage>(attribute, CancellationToken.None))
.ReturnsAsync(mockedResult);
// act
var target = await mock.Object.BindAsync<BrokeredMessage>(attribute);
// assert
target.Should().NotBeNull();
target.Label.Should().Be("whatever");
}
我了解您的担忧可能是完整的集成测试。你似乎想测试整个链。在这种情况下,进行单元测试可能会很困难,因为您依赖于外部系统。如果是这种情况,您可能需要在其上创建一个单独的集成测试,方法是设置一个单独的实例。
考虑到您的功能设置为HttpTrigger
,以下内容应该有效:
# using azure functions cli (2.x), browse to the output file
cd MyAzureFunction/bin/Debug/netstandard2.0
# run a new host/instance if your function
func host start
接下来,只需对托管端点执行http请求:
$ [POST] http://localhost:7071/api/HttpTriggerCSharp?name=my-func
在这种情况下,您有一个干净且隔离的集成设置。
无论哪种方式,我都想争论要么使用模拟单元测试的路线,要么为它设置单独的集成测试设置。
希望这会有所帮助......