使用 Moq 和 MSTest 测试异常的正确方法

Correct method for testing for an exception using Moq and MSTest

对于 Moq 与 MsTest 的行为有点混淆。

编辑:这不是 "How do I test?" 或 "How do I assert?" 的问题,这是查看最小起订量如何工作的草稿本,因此不要关注异常类型等。

我认为更好的问题可能是 => "Does Moq Throws<> behave similar to MsTest ExpectedExceptionAttribute?" 也就是说,他们期望测试或 SUT 中出现异常?

我想知道当与 MsTest 一起使用时,MoQ "Throws" 是如何工作的。不使用 MsTest 预期的异常属性是否更好?在测试中执行 try..catch 是否更好?关于这个我还有几个问题。

我正在模拟数据库调用,当发生错误时我想 return 零 (0)。

TestMethod 直接使用 MsTest 异常属性,并使用 Moq 抛出异常。它仅在我在 SaveCart 方法中抛出异常时有效,而不是在我 return 为零时有效。

我想了解底层行为,因为感觉好像我不应该,也不想在 SaveCart 方法中抛出异常。

这是问题中的测试:

[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void CartRepoSaveCartExceptionShouldReturnZero()
{
     _cartDatabaseMock.Setup(c => c.SaveCart(_cart))
                                   .Throws<ApplicationException>();

    var result = _cartRepository.SaveCart(_cart);

    Assert.AreEqual(result, _cartSaveExceptionValue);
}

这是基本的 SaveCart,它不会引发导致测试失败的异常:

public long SaveCart(Cart cart )
{
    long returnValue;

    try
    {
        returnValue = _cartDatabase.SaveCart(cart);
    }
    catch (Exception)
    {
        return 0;
    }
    return returnValue;
}

这是一个基本的 SaveCart,在其中测试有效,因为它抛出了异常:

public long SaveCart(Cart cart )
{
    long returnValue;

    try
    {
        returnValue = _cartDatabase.SaveCart(cart);
    }
    catch (Exception)
    {
        throw new ApplicationException();
    }
    return returnValue;
}

如果问题解释得不够清楚,请随时为问题建议一个更好的标题。

catch (Exception)
        {
            return 0;
        }

你不是在抛出异常,而是在吞下异常,那么你为什么会期望异常?与MOQ无关。您的测试和代码不同步。

顺便说一下,这是一个不好的做法,吞下异常。

catch (Exception)
        {
            throw new ApplicationException();
        }

这也是一种代码味道。您正在捕获各种异常,然后抛出不同类型的异常。

当被测单元抛出异常时,您应该使用ExpectedExceptionAttribute

在您的第一个示例中,方法没有抛出任何异常,因此测试失败。

由于您的测试方法不会抛出任何异常,因此您根本不需要使用此属性...(在这种情况下只需验证 return 值)

当您想要验证是否抛出了异常并且想要验证是否发生了一些额外的操作时,请使用以下模式:

[TestMethod]
[ExpectedException(typeof(<The specific exception>))]
public void FooTest()
{
    //arrange

    try
    {
       // act
    }
    catch(<the specific exception>)
    {
       // some asserts
       throw;
    }
}

如果出现以下情况,上述代码段将失败:

  1. 引发了错误的异常
  2. 没有引发异常
  3. 你的一个断言失败了。

顺便说一句,由于您在方法中的捕获不是 Exception 而不是 ApplicationException,我建议您将设置更改为:

_cartDatabaseMock.Setup(c => c.SaveCart(_cart)).Throws<Exception>();

你是对的 - 第二个测试 "SaveCart" 有效,因为它抛出异常,而第一个测试失败,因为你正在转向 0。从你对之前答案的回应,我相信你已经知道所有这个。如果你问的是第一次测试失败的行为......它是这样的:

  1. 已调用 SaveCart
  2. 它 returns 一个例外(最小起订量设置的结果)
  3. 您的 try catch 捕获了异常(您故意这样做是为了改变结果)
  4. 你的 try catch returns 0(结果现在是 0,因为你打算改变它)
  5. Assert 根据 _cartSaveExceptionValue
  6. 检查您的结果
  7. 你得到一个失败测试,​​说明类似于此 "Message: Assert.AreEqual failed. Expected. Actual<0 (System.Int32)>."

如果你想仔细检查一下...你可以试试下面的测试

  1. 注释掉 [ExpectedException(typeof())]
  2. 将 Assert.AreEqual(result, _cartSaveExceptionValue) 更改为 Assert.AreEqual(result, 0);
  3. 测试应该通过,因为您正在比较 "result"(又名 0)和 0

我希望这能回答您的问题。