【问题标题】:Unit testing controller methods which return IActionResult返回 IActionResult 的单元测试控制器方法
【发布时间】:2017-05-08 15:44:55
【问题描述】:

我正在构建 ASP.NET Core WebAPI,并且正在尝试为控制器编写单元测试。我发现的大多数示例都来自较旧的 WebAPI/WebAPI2 平台,并且似乎与新的 Core 控制器无关。

我的控制器方法返回IActionResults。但是,IActionResult 对象只有一个需要控制器上下文的 ExecuteResultAsync() 方法。我是手动实例化控制器,所以这个实例中的控制器上下文为空,这会导致调用ExecuteResultAsync时出现异常。从本质上讲,这使我走上了一条非常糟糕的道路,以使这些单元测试成功完成并且非常混乱。我想知道必须有一种更简单/正确的方法来测试 API 控制器。

另外,我的控制器不会使用 async/await,如果这有影响的话。

我想要实现的简单示例:

控制器方法:

[HttpGet(Name = "GetOrdersRoute")]
public IActionResult GetOrders([FromQuery]int page = 0)
{
     try
     {
        var query = _repository.GetAll().ToList();

        int totalCount = query.Count;
        int totalPages = (int)Math.Ceiling((double)totalCount / pageSize) - 1;
        var orders = query.Skip(pageSize * page).Take(pageSize);

        return Ok(new
        {
           TotalCount = totalCount,
           TotalPages = totalPages,

           Orders = orders
        });
     }
     catch (Exception ex)
     {
        return BadRequest(ex);
     }
}

单元测试:

[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk()
{
     // arrange
     var controller = new OrdersController(new MockRepository());

     // act
     IActionResult result = controller.GetOrders();

     // assert
     Assert.Equal(HttpStatusCode.OK, ????);
}

【问题讨论】:

  • 显示GetOrders 方法。你在那个方法中返回什么。将结果转换为您在方法中返回的类型并对其执行断言。

标签: c# unit-testing asp.net-core .net-core asp.net-core-webapi


【解决方案1】:

假设类似

public IActionResult GetOrders() {
    var orders = repository.All();
    return Ok(orders);
}

本例中的控制器返回一个OkObjectResult 类。

将结果转换为您在方法中返回的类型并对其执行断言

[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
    // arrange
    var controller = new OrdersController(new MockRepository());

    // act
    var result = controller.GetOrders();
    var okResult = result as OkObjectResult;

    // assert
    Assert.IsNotNull(okResult);
    Assert.AreEqual(200, okResult.StatusCode);
}

【讨论】:

  • 您好,Nkosi :) 我可以以某种方式将整个结果对象与预期的对象进行比较,这样我就可以检查返回码和对象了吗?现在, Assert.AreEqual(actual, expected) 似乎不起作用。编辑:对象是一个字符串。我必须做断言吗?
  • @Squirrelkiller 那是因为它们是两个独立的实例。您可以从结果中提取对象并在您控制两者的情况下进行比较。
  • @Squirrelkiller 例如Assert.AreEquan(myObject, okResult.Value); 其中myObject 被假定为从您的模拟返回并在控制器操作中传递给Ok()
  • 当您只返回没有内容的 Ok() 并且它正在重新调整 OkResult 而不是 OkObjectResult 时,就会出现此问题。
  • 当转换为 OkObjectResult 时,它的状态码不是总是 200 吗?
【解决方案2】:

您还可以做一些很酷的事情,例如:

    var result = await controller.GetOrders();//
    var okResult = result as ObjectResult;

    // assert
    Assert.NotNull(okResult);
    Assert.True(okResult is OkObjectResult);
    Assert.IsType<TheTypeYouAreExpecting>(okResult.Value);
    Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode);

谢谢

【讨论】:

    【解决方案3】:

    建议将其他答案转换为ObjectResult,但仅当您返回OkObjectResult \ NotFoundObjectResult \ 等时它才有效。但服务器可以返回NotFound\ OkResult,它派生自StatusCodeResult

    例如:

    public class SampleController : ControllerBase
    {
        public async Task<IActionResult> FooAsync(int? id)
        {
            if (id == 0)
            {
                // returned "NotFoundResult" base type "StatusCodeResult"
                return NotFound();
            }
    
            if (id == 1)
            {
                // returned "StatusCodeResult" base type "StatusCodeResult"
                return StatusCode(StatusCodes.Status415UnsupportedMediaType);
            }
    
            // returned "OkObjectResult" base type "ObjectResult"
            return new OkObjectResult("some message");
        }
    }
    

    我查看了所有这些方法的实现,发现它们都继承自IStatusCodeActionResult接口。看起来这是包含StatusCode的最基本类型:

    private SampleController _sampleController = new SampleController();
    
    [Theory]
    [InlineData(0, StatusCodes.Status404NotFound)]
    [InlineData(1, StatusCodes.Status415UnsupportedMediaType)]
    [InlineData(2, StatusCodes.Status200OK)]
    public async Task Foo_ResponseTest(int id, int expectedCode)
    {
        var actionResult = await _sampleController.FooAsync(id);
        var statusCodeResult = (IStatusCodeActionResult)actionResult;
        Assert.Equal(expectedCode, statusCodeResult.StatusCode);
    }
    

    【讨论】:

      【解决方案4】:

      您也可以使用 ActionResult 类作为控制器结果(假设您有 Orders 类型)。 在这种情况下,您可以使用以下内容:

      [ProducesResponseType(typeof(Orders), StatusCodes.Status200OK)]
      public ActionResult<Orders> GetOrders()
      {
          return service.GetOrders();
      }
      

      现在在单元测试中你有:

      Assert.IsInstanceOf<Orders>(result.Value);
      

      另外,这是微软的推荐——https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.2#actionresultt-type

      不幸的是,我不知道为什么要使用 Ok 方法

      return Ok(service.GetOrders());
      

      没有正确映射它。

      【讨论】:

        【解决方案5】:

        这样做的好方法是这样的:

        [Fact]
        public void GetOrders_WithOrdersInRepo_ReturnsOk() {
            // arrange
            var controller = new OrdersController(new MockRepository());
        
            // act
            var result = controller.GetOrders();
        
            // assert
            var okResult = Assert.IsType<OkObjectResult>(result);
            Assert.IsNotNull(okResult);
            Assert.AreEqual(200, okResult.StatusCode);
        }
        

        【讨论】:

          【解决方案6】:
          public async Task CallRxData_ReturnsHttpNotFound_ForInvalidJobNum_ReturnsStoredRxOrder()
          {
              var scanInController = new ScanInController(_logger, _scanInService);
              var okResult = await scanInController.CallRxData(rxOrderRequest);
              var notFoundResult = await scanInController.CallRxData(invalidRxOrderRequest);
              var okResultWithScanInCheckFalse = await scanInController.CallRxData(rxOrderRequest);
              var okResultWithEmptyAelAntiFakeDatas = await scanInController.CallRxData(rxOrderRequest);
              // Assert
              Assert.That(okResult, Is.TypeOf<OkObjectResult>());
              Assert.That(notFoundResult, Is.TypeOf<NotFoundObjectResult>());
              Assert.IsFalse(((okResultWithScanInCheckFalse as ObjectResult).Value as RxOrder).IsSecurity);`enter code here`
          }
          

          【讨论】:

            猜你喜欢
            • 2022-07-27
            • 2021-12-16
            • 1970-01-01
            • 1970-01-01
            • 2023-03-15
            • 1970-01-01
            • 2014-11-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多