【问题标题】:FakeItEasy ControllerTest HttpGet CallsFakeItEasy ControllerTest HttpGet 调用
【发布时间】:2020-05-07 12:54:12
【问题描述】:

我想开始使用 FakeItEasy 来测试查询。 我要编写的测试应该检查 HttpGet 调用是否返回实体(获取所有并通过 Id 获取)

控制器:

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

TestClass:

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(不是我在 testmethod 的第一个 Call.To 中设置的 10 个项目,而是一个不产生结果的枚举)。

由于 result.Any() 为 false,控制器抛出 InvalidOperationException 我什至不能断言结果。IsNotNull()

我要测试的第二个测试是否在调用 API 时返回 1 个项目。 我为参数设置(a)一个 int 类型的 itemId, (b)一个模拟(?)列表,其中包含来自设置的 1 个项目,项目 ID 和 (c) 对 mediatR 的调用应该从 (b) 中返回模拟的 List

我从测试中调用,在调试中我看到调用 await _mediator.Sent() 返回 ToDoItem 的一个 Fake Ienumerable,result 不为 null,而是因为 result.Any() 为 false, 该项目没有被退回,我得到另一个 InvalidOperationException

我觉得我在测试设置中遗漏了一些东西.. 假数据库接口? 我不想碰碰我的控制器并减少 if 限制,这样我的测试就会通过

编辑: 即使我将 if 条件更改为删除 Any 条件 我看到测试进入控制器,返回结果的“第一个或默认值”, 测试在 A Call To Must Have Happened 上失败。 预计会找到一次或多次,但没有调用假对象。 这我真的不明白,我真的看到他在打电话?!

我浏览了 GitHub 以查找示例,但我找到的最接近的是带有方法的实体,这些方法是在接口中定义的。这里不是这样的

看到官方文档并没有让我变得更聪明,我转向 SO

【问题讨论】:

    标签: testing asp.net-core-3.0 mediator fakeiteasy mediatr


    【解决方案1】:

    在第一个测试中,您使用AllToDoItems 类型的参数配置对Send 的调用。但在控制器中,您实际上使用TodoItemsQuery 调用Send。所以调用不匹配,默认(未配置)行为,即返回一个假的IEnumerable,适用。您需要像这样配置调用:

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

    在第二个测试中,问题是一样的,用ToDoItemById而不是AllToDoItems

    【讨论】:

    • 非常感谢!非常有见地的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多