【问题标题】:Correct method for testing for an exception using Moq and MSTest使用 Moq 和 MSTest 测试异常的正确方法
【发布时间】:2016-05-18 23:27:04
【问题描述】:

Moq 与 MsTest 的行为有点混淆。

编辑:这不是“我如何测试?”的问题。或“我如何断言?”,这是一个草稿本,可以查看 MoQ 是如何工作的,所以不要关注异常类型等。

我认为更好的问题可能是 =>“Moq Throws 的行为是否类似于 MsTest ExpectedExceptionAttribute?”也就是说,他们期望测试或 SUT 中出现异常?

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

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

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

我想了解底层行为,因为感觉好像我不应该,也不想在 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;
}

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

【问题讨论】:

  • 你可能想看看这个:stackoverflow.com/questions/16053433/…
  • 我似乎找不到任何起订量文档,快速入门几乎没有 github.com/Moq/moq4/wiki/Quickstart。而且文档只有一个简单的例子nudoq.org/#!/Packages/Moq/Moq/IThrows/M/Throws我想我的困惑在于起订量异常是如何工作的。看起来被测试的方法必须抛出一个异常才能冒泡到测试方法,然后冒泡到MsTest 属性,但这让我很困惑。
  • 我认为您的标题有点误导其他人提供测试起订量的正确方法。如果您想知道导致测试失败或通过的幕后情况。我建议你想出一个更好的标题。此处发布的所有答案对于测试起订量异常的正确方法都是完全有效的。无论如何,我认为您的问题是您的测试通过/失败的潜在行为。我试了一下并发布了答案。
  • 我确信标题可能具有误导性,正如我在问题中指出的那样。我真的不知道 MoQ “扔”在做什么。
  • 那么我发布的答案是否回答了您的问题,或者您仍然感到困惑,或者我误解了您的问题?无论如何,让我知道。我会尽力提供帮助。

标签: c# .net unit-testing moq mstest


【解决方案1】:

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

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

由于您的被测方法没有抛出任何异常,您根本不需要使用此属性...(只需验证此场景中的返回值)

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

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

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

如果出现以下情况,上述 sn-p 将失败:

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

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

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

【讨论】:

  • 感谢您的回答。我看到每个人都在键入异常类型,这不是我要问的问题。我精通这一点,这是一个代码便笺簿,用于查看 MoQ 的工作原理。我不需要帮助编写测试、断言或捕获异常。只是想弄清楚如果我将 Moq 与 MsTest 一起使用,我应该使用属性还是使用 MoQ throws?
  • @SamRabeeh 我没有说例外是问题。我解释了以下方法: 1. 将 ExpectedException 与 Moq 结合起来。 2.当你想模拟你的fake对象抛出异常的场景时怎么办。
  • @SamRabeeh 正如我所写,当您的测试单元引发异常时,请使用ExpectedExceptionAttribute。当您想要模拟依赖项引发异常的场景时,请使用 MoQ throws。当您的被测单元在您的依赖项抛出异常后引发异常时,将它们组合在一起......
  • 你这个狡猾的老狐狸!我刚刚编辑了这个问题以更好地解释它。我想知道我是否应该将问题的标题更改为更合适的名称?
  • 如上所述,我最终完全删除了 MsTest 属性。我赞成你的回答,但我希望我能选择两者作为答案,因为它们都有帮助。
【解决方案2】:

您是对的 - 第二个测试“SaveCart”有效,因为它引发了异常,而第一个测试失败,因为您转为 0。从您对先前答案的回复中,我相信您已经知道所有这些。如果您要询问第一次测试失败的行为......它是这样的:

  1. SaveCart 被调用
  2. 它返回一个异常(您的起订量设置的结果)
  3. 您的 try catch 捕获了异常(您这样做是为了改变结果)
  4. 您的 try catch 返回 0(结果现在是 0,因为您打算更改它)
  5. Assert 根据 _cartSaveExceptionValue 检查您的结果
  6. 您会收到一个失败测试,​​说明类似于“消息:Assert.AreEqual 失败。预期。实际。”

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

  1. 注释掉 [ExpectedException(typeof())]
  2. 将 Assert.AreEqual(result, _cartSaveExceptionValue) 更改为 Assert.AreEqual(result, 0);
  3. 测试应该通过,因为您将“结果”(又名 0)与 0 进行比较

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

【讨论】:

  • 是的,先生,您的评估很准确。我删除了 MsTest 属性,并在方法之间同步了异常类型,瞧。我仍然觉得对 MoQ 有更多的了解。是否有明确的文档来源?我在上面的问题上用几个链接评论了我的问题,但说它们“瘦”是给了他们比他们应得的更多的信任。
  • 我想我明白你的困惑来自哪里。实际上,MSTest 框架没有“抛出”断言。与 xUnit 或 nUnit 等其他框架中可用的 Assert.Throw() 相比,属性 [ExpectedException(typeof())] 是 MSTest 提供的最接近的东西。如果您想验证是否引发了特定异常,您可能需要查看开箱即用的 nUnit/xUnit。
  • 这里有一些选项:1) 如果您想使用 MSTest 进一步探索这个场景,可以通过扩展或编写自己的 try catch 来完成,就像本文中其他人指出的那样 (@987654321 @) 2) 如果你想纯粹使用 Moq 方式来检查异常,你想做类似“mock.Verify”的事情 3) 如果你想同时使用 MStest 和 Moq,那么做你正在做的事情......模拟一个抛出异常,然后断言结果。 4) 查看 nUnit 或 xUnit
  • 这一行是最小起订量: _cartDatabaseMock.Setup(c => c.SaveCart(_cart)) .Throws();这一行是 MSTest at work Assert.AreEqual(result, _cartSaveExceptionValue);
  • 每个人和他们的狗似乎都喜欢 NUnit,我的大部分合同都使用它。我最常听到的原因是测试用例的使用。然而,我更喜欢可以保存在数据库中的 mstest 的数据源驱动测试。由于设置困难,我也反对这一点,我不知道为什么。我可以在几分钟内完成设置。
【解决方案3】:
catch (Exception)
        {
            return 0;
        }

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

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

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

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

【讨论】:

  • 我意识到“返回 0”正在吞噬异常。如果这是生产代码,它将通过日志记录或其他一些机制等进行处理。这不是如何使用 Moq 和 MsTest 处理异常的问题。如果我抛出将冒泡到 MsTest 属性的异常,一切都很好。我认为。并且通用的异常是 AppicationException 再次仅用于学习 MoQ 的这个练习。不要优雅地处理它。我不确定您的答案在哪里,或者您是否只是想指出其他项目中的错误?
  • @SamRabeeh,答案就在那里,你没有抛出任何异常,所以 mstest ExpectedException 不起作用。正如它所说,您期望具有该属性的异常,但在您的代码中您没有抛出异常。不确定这是否会更明显。
  • 删除了我的评论,因为我更新了问题详细信息以减轻混乱。我不需要异常处理或测试方面的教程。只需帮助起订量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2015-02-13
  • 1970-01-01
  • 2014-12-04
  • 2015-03-05
  • 2014-01-18
相关资源
最近更新 更多