Как получить значения из задачи <IActionResult>, возвращенной через API для модульного тестирования - PullRequest
0 голосов
/ 11 июня 2018

Я создал API, используя ASP.NET MVC Core v2.1.Один из моих HttpGet методов настроен следующим образом:

public async Task<IActionResult> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        return Ok(configuration);
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

При модульном тестировании я могу проверить, что ответом был Ok, но мне действительно нужно увидеть значения конфигурации.Кажется, я не могу заставить это работать со следующим:

[TestMethod] 
public void ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();
    var actionResult = controller.GetConfiguration(12);

    Assert.IsTrue(true);
    context.Dispose();
}

Во время выполнения я могу проверить, что actionResult имеет определенные значения, для которых я не могу кодировать.Есть ли что-то, что я делаю не так?Или я просто думаю об этом неправильно?Я хотел бы иметь возможность сделать:

Assert.AreEqual(12, actionResult.Values.ConfigurationId);

Ответы [ 4 ]

0 голосов
/ 19 ноября 2018

Поскольку моя репутация не позволяет мне комментировать ответ @DavidG, который идет в правильном направлении, я приведу пример того, как получить значение внутри Task<IActionResult>.

Как @ Кристофер Дж.Рейнольдс отметил, что actionResult.Value можно увидеть при времени выполнения , но не при компиляции .

Итак, я покажу базовый тест, в котором получим Values:

[TestMethod]
public async Task Get_ReturnsAnArea()
{
    // Arrange
    string areaId = "SomeArea";
    Area expectedArea = new Area() { ObjectId = areaId, AreaNameEn = "TestArea" };

    var restClient = new Mock<IRestClient>();
    restClient.Setup(client => client.GetAsync<Area>(It.IsAny<string>(), false)).ReturnsAsync(expectedArea);

    var controller = new AreasController(restClient.Object);

    //// Act

    // We now await the call
    IActionResult actionResult = await controller.Get(areaId);

    // We cast it to the expected response type
    OkObjectResult okResult = actionResult as OkObjectResult;

    // Assert

    Assert.IsNotNull(okResult);
    Assert.AreEqual(200, okResult.StatusCode);

    Assert.AreEqual(expectedArea, okResult.Value);

   // We cast Value to the expected type
    Area actualArea = okResult.Value as Area;
    Assert.IsTrue(expectedArea.AreaNameEn.Equals(actualArea.AreaNameEn));
}

Конечно, это можно улучшить, но я просто хотел показать вам простой способ получить его.

Надеюсь, это поможет.

0 голосов
/ 11 июня 2018

Хорошая практика предполагает, что в ваших действиях контроллера нет большого количества кода для тестирования, а основная часть логики находится в разъединенных объектах в других местах, которые гораздо проще тестировать.Сказав это, если вы все еще хотите проверить свои контроллеры, то вам нужно сделать свой тест async и ждать вызовов.

Одна из проблем, с которыми вы столкнетесь, заключается в том, что вы используете IActionResult какпозволяет вернуть BadRequest(...) и Ok(...).Однако, поскольку вы используете ASP.NET MVC Core 2.1, вы можете вместо этого начать использовать новый тип ActionResult<T>.Это должно помочь в тестировании, поскольку теперь вы можете получить прямой доступ к строго типизированному возвращаемому значению.Например:

//Assuming your return type is `Configuration`
public async Task<ActionResult<Configuration>> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        // Note we are now returning the object directly, there is an implicit conversion 
        // done for you
        return configuration;
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

Обратите внимание, что теперь мы возвращаем объект напрямую, поскольку существует неявное преобразование из Foo в ActionResult<Foo>

Теперь ваш тест можетвыглядеть так:

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();

    // We now await the call
    var actionResult = await controller.GetConfiguration(12);

    // And the value we want is now a property of the return
    var configuration = actionResult.Value;

    Assert.IsTrue(true);
    context.Dispose();
}
0 голосов
/ 12 июня 2018

Вы можете получить протестированный контроллер без изменения возвращаемого типа.
IActionResult является базовым типом для всех остальных.
Привести результат к ожидаемому типу и сравнить возвращаемое значение с ожидаемым.

Поскольку вытестирование асинхронного метода, также сделайте метод тестирования асинхронным.

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    using (var context = GetContextWithData())
    {
        var controller = new ConfigurationSearchController(context);
        var items = context.Configurations.Count();

        var actionResult = await controller.GetConfiguration(12);

        var okResult = actionResult as OkObjectResult;
        var actualConfiguration = okResult.Value as Configuration;

        // Now you can compare with expected values
        actualConfuguration.Should().BeEquivalentTo(expected);
    }
}
0 голосов
/ 11 июня 2018

Вам нужно дождаться вызова GetConfiguration, чтобы вернуть объект IActionResult следующим образом:

var actionResult = await controller.GetConfiguration(12);

Для этого вам нужно также изменить сигнатуру вашего метода теста на асинхронную.Так что измени это:

public void ConfigurationSearchGetTest()

На это:

public async Task ConfigurationSearchGetTest()
...