【发布时间】:2022-08-19 04:50:57
【问题描述】:
我正在尝试调整 AF 3.0/.NET Core 3.1/xUnit 的 Microsoft 示例(请参阅 Strategies for testing your code in Azure Functions)以使用 AF 3.0/.NET 5.0/xUnit。但是,我遇到了编译问题。
Azure Function 是一个简单的 HTTP 触发器(仅限 GET),ExportFuncApp.csproj 文件如下:
<Project Sdk=\"Microsoft.NET.Sdk\">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include=\"Microsoft.Azure.Functions.Worker.Extensions.Http\" Version=\"3.0.12\" />
<PackageReference Include=\"Microsoft.Azure.Functions.Worker.Sdk\" Version=\"1.2.0\" OutputItemType=\"Analyzer\" />
<PackageReference Include=\"Microsoft.Azure.Functions.Worker\" Version=\"1.5.2\" />
</ItemGroup>
<ItemGroup>
<None Update=\"host.json\">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update=\"local.settings.json\">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
ExportFunc.cs 文件如下:
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
namespace ExportFuncApp
{
public class ExportFunc
{
[Function(nameof(ExportFunc))]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, \"get\")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger(\"ExportFunc\");
logger.LogInformation(\"C# HTTP trigger function processed a request.\");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add(\"Content-Type\", \"text/plain; charset=utf-8\");
response.WriteString(\"Welcome to Azure Functions!\");
return response;
}
}
}
那里没什么特别的。但是,Microsoft (.NET Core 3.1) 提供的 XUnit 测试并不真正适用于 .NET 5.0。有一篇关于这个主题的 StackOverflow 文章:Testing an Azure Function in .NET 5。文章中给出了 4 个解决方案,它们都存在编译问题。给出的第一个解决方案是(ExportFuncUnitTests2.cs):
using Xunit;
using ExportFuncApp;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Moq;
using Microsoft.Azure.Functions.Worker;
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.Worker.Http;
namespace ExportFuncAppUnitTestsXunit
{
public class ExportFuncUnitTests2
{
[Fact]
public async Task Http_trigger_should_return_known_string()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<ILoggerFactory, LoggerFactory>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var context = new Mock<FunctionContext>();
context.SetupProperty(c => c.InstanceServices, serviceProvider);
var byteArray = Encoding.ASCII.GetBytes(\"test\");
var bodyStream = new MemoryStream(byteArray);
var request = new Mock<HttpRequestData>(context.Object);
request.Setup(r => r.Body).Returns(bodyStream);
request.Setup(r => r.CreateResponse()).Returns(() =>
{
var response = new Mock<HttpResponseData>(context.Object);
response.SetupProperty(r => r.Headers, new HttpHeadersCollection());
response.SetupProperty(r => r.StatusCode);
response.SetupProperty(r => r.Body, new MemoryStream());
return response.Object;
});
var result = await ExportFunc.Run(request.Object, context.Object);
result.HttpResponse.Body.Seek(0, SeekOrigin.Begin);
// Assert
var reader = new StreamReader(result.HttpResponse.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.NotNull(result);
Assert.Equal(HttpStatusCode.OK, result.HttpResponse.StatusCode);
Assert.Equal(\"Hello test\", responseBody);
}
}
}
这会导致 ExportFuncUnitTests2.cs 中出现编译错误:
CS1061 \'HttpResponseData\' does not contain a definition for \'GetAwaiter\' and no accessible extension method \'GetAwaiter\' accepting a first argument of type \'HttpResponseData\' could be found (are you missing a using directive or an assembly reference?)
for:
var result = await ExportFunc.Run(request.Object, context.Object);
文章中给出的第二种解决方案涉及到 FakeHttpRequestData.cs:
using System;
using System.Collections.Generic;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using System.IO;
using System.Security.Claims;
namespace ExportFuncAppUnitTestsXunit
{
class FakeHttpRequestData : HttpRequestData
{
public FakeHttpRequestData(FunctionContext functionContext, Uri url, Stream body = null) : base(functionContext)
{
Url = url;
Body = body ?? new MemoryStream();
}
public override Stream Body { get; } = new MemoryStream();
public override HttpHeadersCollection Headers { get; } = new HttpHeadersCollection();
public override IReadOnlyCollection<IHttpCookie> Cookies { get; }
public override Uri Url { get; }
public override IEnumerable<ClaimsIdentity> Identities { get; }
public override string Method { get; }
public override HttpResponseData CreateResponse()
{
return new FakeHttpResponseData(FunctionContext);
}
}
}
还有,FakeHttpResponseData.cs:
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using System.Net;
using System.IO;
namespace ExportFuncAppUnitTestsXunit
{
class FakeHttpResponseData : HttpResponseData
{
public FakeHttpResponseData(FunctionContext functionContext) : base(functionContext)
{
}
public override HttpStatusCode StatusCode { get; set; }
public override HttpHeadersCollection Headers { get; set; } = new HttpHeadersCollection();
public override Stream Body { get; set; } = new MemoryStream();
public override HttpCookies Cookies { get; }
}
}
测试是(ExportFuncUnitTests2.cs):
using Xunit;
using ExportFuncApp;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Moq;
using Microsoft.Azure.Functions.Worker;
using System;
using System.Net;
using Microsoft.Extensions.Logging.Abstractions;
namespace ExportFuncAppUnitTestsXunit
{
public class ExportFuncUnitTests2
{
[Fact]
public async Task Http_trigger_should_return_known_string()
{
// Arrange
var body = new MemoryStream(Encoding.ASCII.GetBytes(\"{ \\\"test\\\": true }\"));
var context = new Mock<FunctionContext>();
var request = new FakeHttpRequestData(
context.Object,
new Uri(\"https://stackoverflow.com\"),
body);
// Act
var function = new ExportFunc(new NullLogger<ExportFunc>());
var result = await function.Run(request, context);
result.HttpResponse.Body.Position = 0;
// Assert
var reader = new StreamReader(result.HttpResponse.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.NotNull(result);
Assert.Equal(HttpStatusCode.OK, result.HttpResponse.StatusCode);
Assert.Equal(\"Hello test\", responseBody);
}
}
}
ExportFuncUnitTests2.cs 有以下编译错误:
CS1729 \'ExportFunc\' does not contain a constructor that takes 1 arguments
for:
var function = new ExportFunc(new NullLogger<ExportFunc>());
和
CS1503 Argument 2: cannot convert from \'Moq.Mock<Microsoft.Azure.Functions.Worker.FunctionContext>\' to \'Microsoft.Azure.Functions.Worker.FunctionContext\'
for:
var result = await function.Run(request, context);
我的文章Guide for running C# Azure Functions in an isolated process 有点用,但在单元测试方面没有帮助。也许我错过了重点。由于没有关于如何使用 Microsoft 的 .NET 5.0 对 AF 3.0 进行单元测试的示例/文档,那么我不应该尝试这样做吗?
任何指针将不胜感激。谢谢!
-
对于第二种解决方案,将类对象初始化为
var function = new ExportFunc();,并将函数初始化为var result = await function.Run(request, context.Object); -
我收到以下编译错误: CS0176 Member \'ExportFunc.Run(HttpRequestData, FunctionContext)\' cannot be access with an instance reference;改为使用类型名称来限定它
-
如果我将第二行更改为: var result = await ExportFunc.Run(request, context.Object);然后我得到: CS1061 \'HttpResponseData\' 不包含 \'GetAwaiter\' 的定义,并且找不到可访问的扩展方法 \'GetAwaiter\' 接受类型为 \'HttpResponseData\' 的第一个参数(您是否缺少使用指令或程序集引用?
标签: unit-testing azure-functions visual-studio-2019 .net-5 xunit