单元测试返回IActionResult的控制器方法

时间:2016-12-22 23:06:01

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

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

我的控制器方法正在返回IActionResults。但是,IActionResult对象只有ExecuteResultAsync()方法,需要控制器上下文。我手动实例化控制器,因此此实例中的控制器上下文为null,这在调用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, ????);
}

6 个答案:

答案 0 :(得分:51)

假设像

这样的东西
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);
}

答案 1 :(得分:3)

建议将其他答案强制转换为ObjectResult,但是只有当您返回OkObjectResult \ NotFoundObjectResult \等时,该方法才能起作用。但是服务器可以返回NotFound \ {{1} },其源自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

答案 2 :(得分:1)

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

    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 :(得分:0)

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

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

现在在单元测试中您已经拥有:

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

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

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

return Ok(service.GetOrders());

映射不正确。

答案 4 :(得分:0)

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

[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);
}

答案 5 :(得分:0)

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`
}