如何对ASP.Net Core 2.2中的CreatedAtAction路由值进行单元测试以停止运行时失败

时间:2018-12-05 19:15:22

标签: c# asp.net-web-api asp.net-core

将我的API从ASP.Net Core 2.1升级到2.2之后,我在运行时遇到了InvalidOperationException异常。

呼叫堆栈

System.InvalidOperationException:
   at Microsoft.AspNetCore.Mvc.CreatedAtActionResult.OnFormatting (Microsoft.AspNetCore.Mvc.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsync (Microsoft.AspNetCore.Mvc.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.ObjectResult.ExecuteResultAsync (Microsoft.AspNetCore.Mvc.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeResultAsync>d__20.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)

原因

这是由于我的CreatedAtAction中缺少参数所致。

public async Task<IActionResult> GetFields([FromRoute]Guid productId)
{
    return Ok(await _prodRepo.GetProductFields(productId));
}

public async Task<IActionResult> CreateFields([FromRoute]Guid productId, [FromBody]ProductField[] fields)
{
    ...
    return CreatedAtAction(nameof(GetFields), fields);
}

解决方案

最后一行应该包含productId路由值,如下所示:

return CreatedAtAction(nameof(GetFields), { productId }, fields);

当路由值名称不匹配时,会发生相同的错误。它并没有在2.1中引起运行时异常,但是我猜想它对依赖返回URL的任何人都是无声的。

更大的问题

如果路由值的名称不正确,则会在运行时发生相同的异常。代码编译时没有错误也没有警告,并且在运行时失败。

我担心的是,这些错误在开发过程中很容易引入,并且很容易遗漏,并且仅在运行时浮出水面。我们有一个开发人员团队积极从事此Web API项目。当一个动作方法的签名更改时,会再次发生(从另一个动作的CreatedAtAction结果中引用)。

如何对这种错误情况进行单元测试?我不想在部署之前必须在所有API端点上运行集成测试来捕获运行时错误。

尝试

我尝试了以下操作,但随后发现我需要一个UrlHelper并卡在此处。

[Fact]
public void Test_CreateFields_CreatedAtAction_Values()
{
    var fields = new ProductFieldRel { ProductId = _productId };
    var task = _controller.CreateFields(_productId, new[] { fields });
    var result = task.Result as CreatedAtActionResult;

    Assert.NotNull(result);

    result.OnFormatting(new ControllerContext
    {
        HttpContext = new DefaultHttpContext { User = _user }
    });
}

0 个答案:

没有答案