Методы контроллера модульного тестирования, возвращающие IActionResult

Я занимаюсь созданием 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, ????);
}

person Jake Shakesworth    schedule 22.12.2016    source источник
comment
Покажите метод GetOrders. что вы возвращаете в этом методе. приведите результат к типу того, что вы возвращаете в методе, и выполните свое утверждение для этого.   -  person Nkosi    schedule 23.12.2016


Ответы (5)


Предполагая что-то вроде

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);
}
person Nkosi    schedule 22.12.2016
comment
Привет снова, Nkosi :) Могу ли я как-нибудь сравнить весь объект результата с ожидаемым, чтобы я мог проверить и код возврата, и объект? Прямо сейчас Assert.AreEqual ‹IActionResult› (фактический, ожидаемый), похоже, не работает. Изменить: объект представляет собой строку. Я должен делать утверждения? - person Squirrelkiller; 01.05.2018
comment
@Squirrelkiller, потому что это два отдельных экземпляра. Вы можете извлечь объект из результата и сравнить его при условии, что вы контролируете и то, и другое. - person Nkosi; 01.05.2018
comment
@Squirrelkiller, например Assert.AreEquan(myObject, okResult.Value);, где myObject предполагается, что это то, что было возвращено из вашего макета и передано Ok() в действии контроллера. - person Nkosi; 01.05.2018
comment
Проблема с этим возникает, когда вы возвращаете просто Ok () без содержимого и перенастраиваете OkResult вместо OkObjectResult. - person Mauro Bilotti; 23.03.2021
comment
При преобразовании в OkObjectResult всегда ли он будет иметь код состояния 200? - person Cedervall; 03.06.2021

Вы также можете делать такие крутые вещи, как:

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

Спасибо

person Ernest    schedule 07.06.2019

В других ответах рекомендуется использовать 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);
}
person n1k1t0ss    schedule 03.02.2020

Вы также можете использовать класс 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-введите

К сожалению, я не знаю, зачем использовать метод ОК

return Ok(service.GetOrders());

не отображает его должным образом.

person Jacek    schedule 09.09.2019

Хороший способ сделать это:

[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);
}
person Alexandre N.    schedule 05.04.2021