【问题标题】:Casting an ActionResult rather than IActionResult to an OKObjectResult for testing a 200 status code?将 ActionResult 而不是 IActionResult 转换为 OKObjectResult 以测试 200 状态代码?
【发布时间】:2022-08-11 20:39:33
【问题描述】:

我打算使用 ActionResult 而不是 IActionResult,以便 Swagger 自动选择我的类型,但我收到一个错误消息,说我无法将 ActionResult 转换为 OkObjectResult。

如何转换为 OKObjectResult 以测试 200 状态代码?

我的 IActionResult 控制器

[HttpGet]
public async Task<IActionResult<IEnumerable<Listing>>> Get()
{
  var listings = await listingService.GetAllListings();
  if (listings.Any())
  {
    return Ok(listings);
  }
  return NotFound();
}

我的 ActionResult 控制器

[HttpGet]
public async Task<ActionResult<IEnumerable<Listing>>> Get()
{
  var listings = await listingService.GetAllListings();
  if (listings.Any())
  {
    return Ok(listings);
  }
  return NotFound();
}

我的测试

[Fact]
public async Task ShouldReturnA200StatusCode()
{
    var res = (OkObjectResult)await sut.Get();

    res.StatusCode.Should().Be(200);
}
  • 您正在测试框架,而不是您的 SUT。如果转换为OkObjectResult 成功,状态码将始终为200。如果它不是200,它必然是不同的类型,并且在实际断言之前测试将失败。
  • 通过“测试框架”,我的意思是您正在验证OkObjectResult 的状态代码为200,这一事实应该(我假设是)由 ASP.NET Core 测试单元测试,而不是你的单元测试。简单地断言res is OkObjectResult 就足够了,尽管这并没有测试它可以返回200 结果的所有可能方式(就此而言,您现在拥有的也没有)。无论哪种方式,您都在测试实现细节。
  • 感谢@madreflection 的反馈。我是 C# 单元测试的新手,还没有找到任何关于如何做到这一点的好信息(目前我在看了一些教程后把这些部分放在一起)。我会喜欢你在这方面拥有的任何资源,所以我测试了 SUT。
  • 在单独的说明中,我不确定 Swagger 如何/为什么会使用 ActionResultIActionResult 以不同方式选择您的类型。使用ProducesResponseType 属性,它将在 OAS3 定义 (swagger.json) 中生成适当的架构信息。
  • 回复:Swagger the docs here 说优点是您不再需要注释(我什至可以省略 Ok() 部分)

标签: c# xunit .net-6.0 web-api-testing


【解决方案1】:

看看我的解决方案,了解如何使用 XUnit 在单元测试中验证 HTTP 状态代码。

[Fact]
public async Task UpdateProduct_When_Product_IsValid_ShouldReturn200()
{
    //Arrange
    ProductDto productDto = new DataGenerator()
        .GenerateProductDto_Valid(1)
        .First();

    var result = _productAppServiceMock.Setup(p => p
    .UpdateAsync(
        It.IsAny<Guid>(),
        It.IsAny<ProductDto>()))
    .ReturnsAsync(() => productDto);

    //Act
    var itemHttp = await productController
        .UpdateProductAsync(productDto.Id, productDto);

    //Assert
    _productAppServiceMock.Verify(p => p.UpdateAsync(It.IsAny<Guid>(),
        productDto), times: Times.Once);

    Assert.Equal(typeof(Microsoft.AspNetCore.Mvc.OkObjectResult), itemHttp.GetType());
}

【讨论】:

    【解决方案2】:

    按照this answer to a similar question 的指导,您需要转换.Get() 方法的Result(而不仅仅是.Get() 方法),然后您可以检查200 OK StatusCode

    
        [Fact]
        public async Task ShouldReturnA200StatusCode()
        {
            var res = await sut.Get();
    
            var okObj = res.Result as ObjectResult;
    
            okObj.StatusCode.Should().Be(StatusCodes.Status200OK);
        }
    

    继上面的 cmets 之后,我使用了 ObjectResult 类型,目的是不强制使用 200 状态代码,并使用了 as 强制转换。

    【讨论】:

    • 切勿使用 as 并在未首先测试它不是 null 的情况下使用结果。如果/当它产生null,下一行将抛出NullReferenceException。这不描述实际的问题作为测试的结果,因此您的测试结果将无法准确反映回归的性质。
    猜你喜欢
    • 2019-08-01
    • 2019-08-01
    • 2013-10-25
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    相关资源
    最近更新 更多