After upgrading my API from ASP.Net Core 2.1 to 2.2, I experienced some InvalidOperationException exceptions at runtime.
Callstack
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)
Cause
This happened due to a missing parameter in my 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);
}
Resolution
The last line should have included the productId
route value as follows:
return CreatedAtAction(nameof(GetFields), { productId }, fields);
The same error occurs when route value names mismatch. It did not cause runtime exceptions in 2.1, but I guess it failed silently for anyone relying on the return URL.
The Bigger Problem
The same exception occurs at runtime if the route values are incorrectly named. The code compiles without error nor warning and fails during runtime.
My concern is that these mistakes are easy to introduce and easy to miss during development, and only surface at runtime. We have a team of developers actively working on this Web API project. It will happen again when a signature of an action method changes, referenced from another actions's CreatedAtAction
result.
How can I unit test for this error case? I do not want to have to run integration tests on all the API endpoints to catch runtime errors before I deploy.
Attempt
I tried the following, but then found I need an UrlHelper and got stuck at this point.
[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 }
});
}