如何使用 Moq 对 WEB API 控制器异常进行单元测试

How to Unit test WEB API Controller Exception using Moq

如何对状态代码为 500 和消息的 IHttpActionResult 异常 InternalServerError 进行单元测试

[HttpGet]
public async Task<IHttpActionResult> Get(Guid myId)
{
    try
    {
        var myaccount = await _myaccountService.GetMyAccount(myId);
        return Ok(myaccount);
    }
    catch (Exception ex)
    {
        return InternalServerError();
    }
}

是否尝试过测试方法

[TestMethod]
public async Task GeMyAccount_WhenThrowsException_ReturnsServerError()
{
    // Arrange         
    var exception = new Exception("Internal Server Error");
    var expectedResult = new List<MyAccount>
    {
        new  MyAccount
        {
            Id = "1",
            Name = "Name1"
        },
        new  MyAccount
        {
            Id = "2",
            Name = "Name2"
        },
    };
    var myId = new Guid();

    //Act 
    var mockMyAccountService = new Mock<IMyAccountService>();
    mockMyAccountService.Setup(mock => 
    mock.GetMyAccount(myId)).Throws(exception).Verifiable();   
    var controller = new MyAccountController(mockMyAccountService.Object);

    //Assert
    var actualResult = (await controller.Get(myId) as 
    OkNegotiatedContentResult<MyAccount>).Content;       
       ?? var result = actualResult as ObjectResult;
       ?? Assert.AreEqual(StatusCodes.Status500InternalServerError, result.StatusCode);
       ?? Assert.AreEqual("Internal Server Error ", result.Value);
      mockMyAccountService.Verify(b => b.GetMyAccount(myId));
}

不确定如何使用 Moq 获取异常和状态代码 500。

看看这段代码

    [Fact]
    public async Task Test1()
    {
        //arrange 
        //prepare your data for test

        //act
        async Task action()
        {
            //run your sut action
        }

        //assert
        var exception = await Assert.ThrowsAsync<Exception>(action);
        Assert.Equal("Internal Server Error", exception.Message);
    }

正如 Nkosi 所说,异常被 try-catch 块吞没,因此您无法对异常做出任何断言。

但是您可以(并且应该)对返回的对象进行断言。

[TestMethod]
public async Task GivenAFaultyMyAccountService_WhenICallGet_ThenItReturnsAnInternalServerError()
{
    //Arrange         
    var expectedException = new Exception("Something went wrong");
    var mockMyAccountService = new Mock<IMyAccountService>();
    
    mockMyAccountService
       .Setup(svc => svc.GetMyAccount(It.IsAny<Guid>()))
       .Throws(expectedException);   
    
    var sut = new MyAccountController(mockMyAccountService.Object);

    //Act 
    var actualResult = await sut.Get(Guid.NewGuid());

    //Assert
    Assert.IsInstanceOfType(actualResult, typeof(InternalServerErrorResult));
}

我对您的测试进行了以下更改:

  • 我已重命名您的测试以符合 Given-When-Then structure
  • 在这个测试用例中,我只关注 单一 情况,当底层依赖发生故障时,所以我摆脱了与快乐之路相关的一切
    • 快乐之路应该有一个单独的测试用例
  • 我通过将 myId 参数替换为 It.IsAny<Guid>()
  • 使模拟 Setup 更通用
  • 我也将 new Guid() 替换为 Guid.NewGuid() 因为前者会创建一个空的 uuid,而后者会生成一个新的 uuid
  • 我删除了 Verifiable 调用,因为这里并不真正需要它
  • Act 阶段是您对给定方法进行实际调用时,而不是在构建控制器时,因此我已将评论移至正确位置
  • 我把变量名controller改成sut,代表System Under Test
  • 我已将难以阅读的评估逻辑替换为简单的类型检查