如何在.NET Core中模拟/存根HttpRequestMessage?

Kev*_*ist 3 c# azure .net-core azure-functions asp.net-core-webapi

我有一个要从Azure Functions v1移植到v2的函数,并且作为其中一部分,我遇到了更新单元测试(创建存根的HttpRequestMessage)的问题。这是针对.NET Framework 4.7的在Function v1上运行的代码

public class FunctionExample
{
    [FunctionName(nameof(SomeFunction))]
    public static async Task<HttpResponseMessage> SomeFunction
    (
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "SomeFunction")]
        HttpRequestMessage request
    )
    {
        var body = await request.Content.ReadAsStringAsync();
        if (string.IsNullOrEmpty(body))
        {
            return request.CreateResponse(HttpStatusCode.BadRequest, "Bad job");
        }
        else
        {
            return request.CreateResponse(HttpStatusCode.OK, "Good job");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

还有我的测试代码

public class FunctionExampleTests
{
    [Test]
    public async Task TestSomeFunction()
    {
        var request = new HttpRequestMessage
        {
            Method = HttpMethod.Post,
            RequestUri = new Uri("http://localhost/"),
            Content = new StringContent("", Encoding.UTF8, "application/json")
        };

        request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey,
            new HttpConfiguration());

        var response = await FunctionExample.SomeFunction(request);

        var content = await response.Content.ReadAsStringAsync();

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
        Assert.That(content, Is.EqualTo("\"Bad job\""));
    }
}
Run Code Online (Sandbox Code Playgroud)

移植到v2后,我的功能项目csproj文件如下所示。唯一的区别是我不再针对完整框架,也不再添加AzureFunctionsVersion节点。

public class FunctionExample
{
    [FunctionName(nameof(SomeFunction))]
    public static async Task<HttpResponseMessage> SomeFunction
    (
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "SomeFunction")]
        HttpRequestMessage request
    )
    {
        var body = await request.Content.ReadAsStringAsync();
        if (string.IsNullOrEmpty(body))
        {
            return request.CreateResponse(HttpStatusCode.BadRequest, "Bad job");
        }
        else
        {
            return request.CreateResponse(HttpStatusCode.OK, "Good job");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是csproj我重新定位到.NET Core 2.0之后的测试项目

public class FunctionExampleTests
{
    [Test]
    public async Task TestSomeFunction()
    {
        var request = new HttpRequestMessage
        {
            Method = HttpMethod.Post,
            RequestUri = new Uri("http://localhost/"),
            Content = new StringContent("", Encoding.UTF8, "application/json")
        };

        request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey,
            new HttpConfiguration());

        var response = await FunctionExample.SomeFunction(request);

        var content = await response.Content.ReadAsStringAsync();

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
        Assert.That(content, Is.EqualTo("\"Bad job\""));
    }
}
Run Code Online (Sandbox Code Playgroud)

以前,我使用此问题的答案来正确存根HttpRequestMessage,但似乎不再起作用。

当我尝试对此进行编译时,出现以下编译错误

Error   CS0246  The type or namespace name 'HttpConfiguration' could not be found (are you missing a using directive or an assembly reference?)
Error   CS0103  The name 'HttpPropertyKeys' does not exist in the current context
Run Code Online (Sandbox Code Playgroud)

因此,如果我只是删除该行,希望不再需要该修复程序

request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
Run Code Online (Sandbox Code Playgroud)

相反,我收到此错误消息

System.InvalidOperationException:HttpRequestMessage实例未正确初始化。使用HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage为当前请求创建HttpRequestMessage。

尝试遵循错误消息的指示对我而言未见成效,我尝试设置HttpContext 类似问题的答案

request.Properties.Add(nameof(HttpContext), new DefaultHttpContext());
Run Code Online (Sandbox Code Playgroud)

但这给了我一个不同的错误(与问题相同)

System.ArgumentNullException:值不能为null。

参数名称:provider

Kev*_*ist 10

Azure Functions在某种程度上基于ASP.NET MVC WebApi,它对.NET Core进行了一些更改。HttpConfiguration例如,在面向.NET Core / Standard的任何程序包中似乎都没有提供。为了解决这个问题,我必须安装一对夫妇的包在我的测试项目,即Microsoft.AspNetCore.Mvc用于AddMvc()Microsoft.AspNetCore.Mvc.WebApiCompatShim.AddWebApiConventions(),其中:

提供ASP.NET Core MVC与ASP.NET Web API 2的兼容性,以简化现有Web API实现的迁移

所以我把这些加到了我的测试项目中

<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.2.0" />
Run Code Online (Sandbox Code Playgroud)

现在我的测试项目看起来像这样

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.2.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\FunctionExampleFunction\FunctionExampleFunction.csproj" />
  </ItemGroup>
</Project>
Run Code Online (Sandbox Code Playgroud)

为了模拟ArgumentNullException所暗示的服务缺失(在这种情况下,我认为是MediaTypeFormatter),我必须从本质上引导MVC来正确初始化HttpContext。

[Test]
public async Task TestSomeFunction()
{
    var request = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        RequestUri = new Uri("http://localhost/"),
        Content = new StringContent("", Encoding.UTF8, "application/json")
    };

    var services = new ServiceCollection()
        .AddMvc()
        .AddWebApiConventions()
        .Services
        .BuildServiceProvider();

    request.Properties.Add(nameof(HttpContext), new DefaultHttpContext
    {
        RequestServices = services
    });

    var response = await FunctionExample.SomeFunction(request);

    var content = await response.Content.ReadAsStringAsync();

    Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
    Assert.That(content, Is.EqualTo("\"Bad job\""));
}
Run Code Online (Sandbox Code Playgroud)

这样就可以编译,运行和通过测试。