FakeItEasy ControllerTest HttpGet Calls

Я хочу начать использовать FakeItEasy для тестирования запросов. Тесты, которые я хочу написать, должны проверять, возвращаются ли объекты при вызовах HttpGet (получить все и получить по идентификатору).

Контроллер:

public class ToDoController : ControllerBase
  {
    private readonly IMediator _mediator;

    public ToDoController(IMediator mediator) =>
    _mediator = mediator;

    [HttpGet]
    [Produces("application/json")]
    [ProducesResponseType(typeof(IEnumerable<ToDoItem>), (int)HttpStatusCode.OK)]
    public async Task<ActionResult<IEnumerable<ToDoItem>>> Get()
    {
        var result = await _mediator.Send(new ToDoItemsQuery(new 
                         AllToDoItems())).ConfigureAwait(false);

        if (result != null && result.Any())
        {
            return result.ToList();
        }

        throw new InvalidOperationException("TODO: error handling");
    }

    [HttpGet]
    [Route("{id}")]
    [Produces("application/json")]
    [ProducesResponseType(typeof(ToDoItem), (int)HttpStatusCode.OK)]
    public async Task<ActionResult<ToDoItem>> GetById(int itemId)
    {
      var result = await _mediator
        .Send(new ToDoItemsQuery(new ToDoItemById(itemId)))
        .ConfigureAwait(false);

      if (result != null && result.Any())
      {
        return result.FirstOrDefault(); 
      }

      throw new InvalidOperationException("TODO: error handling");
    }
  }
}

Тестовый класс:

public class ToDoItemControllerTests : ControllerTestBase
{
  private IMediator _mediator;

  private ToDoController _sut;

  public ToDoItemControllerTests()
  {
    _mediator = A.Fake<IMediator>();
    _sut = new ToDoController(_mediator);
  }

  [TestMethod]
  public async Task GetAllItemsAsync_SuccessTest()
  {
    A.CallTo(() => _mediator.Send(A<AllToDoItems>._, 
           A<CancellationToken>._)).Returns(A.CollectionOfFake<ToDoItem>(10));
    var result = await _sut.Get();

    Assert.IsNotNull(result);
    A.CallTo(() => _mediator).MustHaveHappened();
  }

  [TestMethod]
  public async Task GetItemByIdAsync_SuccessTest()
  {
    // Arrange
    int itemId = 2;
    var commandResult =
      new List<ToDoItem>
      {
        new ToDoItem
        {
          Id = itemId        
        };
      }

    A.CallTo(() => MediatR.Send(A<ToDoItemById>._, A<CancellationToken>._)).Returns(commandResult);

    // Act
    var result = await _sut.GetById(itemId);

    // Assert
    Assert.IsNotNull(result);
    A.CallTo(() => MediatR.Send(A<ToDoItemById>._, A<CancellationToken>._)).MustHaveHappened();
  }
}

Итак, в первом тесте я настроил A.CallTo интерфейса IMediatR для возврата 10 элементов ToDoItem. Во время отладки я вижу, как _sut.Get() входит в контроллер, вводя правильный вызов метода/API. _mediator.Send() в контроллере возвращает Fake IEnumerable (не 10 элементов, которые я установил в первом Call.To в тестовом методе, а перечисление, которое не дает результатов).

Из-за того, что result.Any() является ложным, контроллер выдает InvalidOperationException, и я даже не могу подтвердить результат.IsNotNull()

Второй тест, который я хочу проверить, возвращается ли 1 элемент при вызове API. Я установил (a) itemId типа int для параметра, (b) имитированный (?) Список с 1 элементом из настройки с itemId и (c) вызов mediatR должен возвращать имитированный Listfrom (b)

Я делаю вызов из теста, в отладке я вижу вызов await _mediator.Sent(), возвращающий Fake Ienumerable of ToDoItem, результат не равен нулю, но поскольку результат.Any() имеет значение false, элемент не возвращается, и я получаю еще одно исключение InvalidOperationException

Я чувствую, что что-то упускаю в настройке тестов. Поддельный интерфейс базы данных? Я не хочу рисковать своим контроллером и делать его менее строгим, просто чтобы мой тест прошел

РЕДАКТИРОВАТЬ: даже если я изменю условие if на удаление условия Any и увижу, что тест входит в контроллер, возвращая результат «Первый или по умолчанию», тест завершается неудачей при вызове, который должен был произойти. Ожидалось, что он будет найден один или несколько раз, но никаких обращений к фальшивому объекту не было. Этого я действительно не понимаю, я действительно вижу, как он звонит?!

Я просмотрел GitHub, чтобы найти примеры, но наиболее близкими, которые я нашел, были сущности с методами, причем эти методы были определены в интерфейсе. Это не тот случай

Поскольку официальная документация не делает меня мудрее, я обращаюсь к SO ‹3 Заранее спасибо!


person BHANG    schedule 07.05.2020    source источник


Ответы (1)


В первом тесте вы настраиваете вызов Send с аргументом типа AllToDoItems. Но в контроллере вы на самом деле вызываете Send с TodoItemsQuery. Таким образом, вызов не совпадает, и применяется поведение по умолчанию (ненастроенное), которое заключается в возврате поддельного IEnumerable. Вам нужно настроить вызов следующим образом:

A.CallTo(() => _mediator.Send(A<TodoItemsQuery>._, 
           A<CancellationToken>._)).Returns(A.CollectionOfFake<ToDoItem>(10));

Во втором тесте та же проблема, только ToDoItemById вместо AllToDoItems

person Thomas Levesque    schedule 07.05.2020
comment
Большое тебе спасибо! Очень проницательный ответ - person BHANG; 08.05.2020