How to unit-test action with return type ActionResult<T>?

倖福魔咒の 提交于 2020-01-05 05:53:09

问题


My question is very similar to this one:

How to unit-test an action, when return type is ActionResult?

The problem is that my question mixes in the generic ActionResult<T> type, async, and Ok(...). I can't seem to adapt linked question's answer to the generic situation. Or possibly my scenario is subtly different.

Here's a repro. Create new ASP.NET Core Web Application of "API" type. Add a new xUnit .NET Core test project to the solution, that references the API project (as well as any needed framework libs). Create the controller and tests like this, respectively:

public class Thing { public string Name => "Foobar"; }
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<Thing>> Get()
    {
        // The real "Thing" would be asynchronously retrieved from the DB
        return Ok(new Thing());
    }
}
[Fact]
public async Task Test1()
{
    var controller = new ValuesController();
    var actionResult = await controller.Get();
    Assert.NotNull(actionResult.Value);
    Assert.Equal("Foobar", actionResult.Value.Name);
}

Instead of turning green, this test fails on the NotNull assertion (or, if I would not have that assertion, it throws a NullReferenceException).

After debugging and inspecting the class hierarchy, I found that this seems to give the desired results:

[Fact]
public async Task Test1()
{
    var controller = new ValuesController();
    var actionResult = await controller.Get();
    var okResult = actionResult.Result as OkObjectResult;
    var realResult = okResult?.Value as Thing;
    Assert.Equal("Foobar", realResult?.Name);
}

But this feels like I'm doing something wrong. Practically, I'm left with two related questions:

  1. Is there another idiomatic way to write this test, that collapses all those as casts?
  2. Why does the first example compile, yet give a runtime exception? What's going on here?

回答1:


With XUnit, you can use T t = Assert.IsType<T>(other). That will do the casting if possible, but otherwise it will cause the test to fail.

For instance I do something like this:

IActionResult actionResult = await Controller.GetItem(id);
OkObjectResult okObjectResult = Assert.IsType<OkObjectResult>(actionResult);
Model model = Assert.IsType<Model>(okObjectResult.Value);
Assert.Equal(id, model.Id);

As for your 2nd question, things can throw null reference exceptions at runtime and compile correctly. That problem will be solved with C# 8 and non-nullable types.



来源:https://stackoverflow.com/questions/53396261/how-to-unit-test-action-with-return-type-actionresultt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!