【问题标题】:Azure Functions (AF) 3.0/.NET 5.0/XUnit Unit Testing - Compilation Errors (C#)Azure Functions (AF) 3.0/.NET 5.0/XUnit 单元测试 - 编译错误 (C#)
【发布时间】: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 \'Ht​​tpResponseData\' 不包含 \'GetAwaiter\' 的定义,并且找不到可访问的扩展方法 \'GetAwaiter\' 接受类型为 \'Ht​​tpResponseData\' 的第一个参数(您是否缺少使用指令或程序集引用?

标签: unit-testing azure-functions visual-studio-2019 .net-5 xunit


【解决方案1】:

ExportFunc 中的 Run() 函数不可等待。因此,您应该替换以下行: var 结果 = 等待函数。运行(请求,上下文) 和: var result = function.Run(request, context)。

如果您想测试可等待函数(即 async Run()),那么您可以使用 await 从单元测试中调用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-24
    • 2017-12-22
    • 2015-06-12
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多