简体   繁体   English

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

[英]Unit testing controller methods which return IActionResult

I'm in the process of building an ASP.NET Core WebAPI and I'm attempting to write unit tests for the controllers.我正在构建 ASP.NET Core WebAPI,并且正在尝试为控制器编写单元测试。 Most examples I've found are from the older WebAPI/WebAPI2 platforms and don't seem to correlate with the new Core controllers.我发现的大多数示例都来自较旧的 WebAPI/WebAPI2 平台,并且似乎与新的 Core 控制器无关。

My controller methods are returning IActionResults .我的控制器方法返回IActionResults However, the IActionResult object only has a ExecuteResultAsync() method which requires a controller context.但是, IActionResult对象只有一个ExecuteResultAsync()方法,该方法需要控制器上下文。 I'm instantiating the controller manually, so the controller context in this instance is null, which causes an exception when calling ExecuteResultAsync .我正在手动实例化控制器,因此此实例中的控制器上下文为空,这会在调用ExecuteResultAsync时导致异常。 Essentially this is leading me down a very hacky path to get these unit tests to successfully complete and is very messy.从本质上讲,这让我走上了一条非常笨拙的道路,以使这些单元测试成功完成并且非常混乱。 I'm left wondering that there must be a more simple/correct way of testing API controllers.我想知道必须有一种更简单/正确的方法来测试 API 控制器。

Also, my controllers are NOT using async/await if that makes a difference.此外,我的控制器不会使用 async/await,如果这有所不同的话。

Simple example of what I'm trying to achieve:我试图实现的简单示例:

Controller method:控制器方法:

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

Unit test:单元测试:

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

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

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

Assuming something like the假设类似

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

the controller in this case is returning an OkObjectResult class.在这种情况下,控制器返回一个OkObjectResult类。

Cast the result to the type of what you are returning in the method and perform your assert on that将结果转换为您在方法中返回的类型并对其执行断言

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

You can also do cool things like:你还可以做一些很酷的事情,比如:

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

Thanks谢谢

Other answers adviced to cast to ObjectResult , but its work only if you return OkObjectResult \\ NotFoundObjectResult \\ etc. But server could return NotFound \\ OkResult which derived from StatusCodeResult .其他答案建议转换为ObjectResult ,但只有当您返回OkObjectResult \\ NotFoundObjectResult \\ 等时它才OkObjectResult 。但是服务器可以返回NotFound \\ OkResult ,它派生自StatusCodeResult

For example:例如:

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

I looked at the implementation of all these methods and found that they are all inherited from the IStatusCodeActionResult interface.我查看了所有这些方法的实现,发现它们都是继承自IStatusCodeActionResult接口。 It seems like this is the most base type that contains StatusCode :似乎这是包含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);
}

You also can use ActionResult class as a controller result (assuming you have type Orders).您还可以使用 ActionResult 类作为控制器结果(假设您有类型 Orders)。 In that case you can use something like this:在这种情况下,您可以使用以下内容:

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

and now in unit tests you have:现在在单元测试中你有:

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

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

Unfortunately, I don't know why using Ok method不幸的是,我不知道为什么使用 Ok 方法

return Ok(service.GetOrders());

doesn't map it properly.没有正确映射它。

A good way to do that is like this:这样做的一个好方法是这样的:

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

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

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