简体   繁体   English

如何使用返回类型ActionResult对动作进行单元测试 <T> ?

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

My question is very similar to this one: 我的问题与此非常相似:

How to unit-test an action, when return type is ActionResult? 返回类型为ActionResult时,如何对动作进行单元测试?

The problem is that my question mixes in the generic ActionResult<T> type, async , and Ok(...) . 问题是我的问题混在通用的 ActionResult<T>类型, asyncOk(...) 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. 创建新的“ API”类型的ASP.NET Core Web应用程序。 Add a new xUnit .NET Core test project to the solution, that references the API project (as well as any needed framework libs). 向解决方案中添加一个新的xUnit .NET Core测试项目,该项目引用该API项目(以及任何所需的框架库)。 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 ). 该测试没有NotNull绿色,而是NotNull断言上失败 (或者,如果我没有该断言,它将抛出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? 是否有另一种惯用的方式来编写此测试,从而将所有测试折叠as演员?
  2. Why does the first example compile, yet give a runtime exception? 为什么第一个示例可以编译,却给出运行时异常? What's going on here? 这里发生了什么?

With XUnit, you can use T t = Assert.IsType<T>(other) . 使用XUnit,可以使用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. 该问题将使用C#8和非空类型解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM