【问题标题】:MOQ - verify exception was thrownMOQ - 验证异常被抛出
【发布时间】:2013-04-09 19:48:36
【问题描述】:

我使用 MOQ 框架进行测试。 我有一个场景,我希望抛出一个错误异常。 如何验证它是否被抛出?

public void Koko(List<string?> list) 
{ 
   foreach(string? str in list) 
   { 
        if (str != null) someProperty.Foo(str); 
        else throw new FormatException(); 
   } 
} 

提前致谢。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    如果您想验证是否引发了异常(由您自己的代码),那么 Moq 不是您选择的工具。 只需使用其中一种可用的单元测试框架。

    Xunit/NUnit:

    Assert.Throws<SomeException>(() => foo.Bar());
    

    流利断言:

    Action act = () => foo.Bar();
    act.Should().Throw<SomeException>();
                   
    

    https://fluentassertions.com/introduction

    http://www.nunit.org/index.php?p=exceptionAsserts&r=2.6.2

    【讨论】:

    • 如果您使用的是最新的 FluentAssertions 包,您将验证抛出的异常如下: Action action = () => foo.Bar(); action.Should().Throw();
    • 如果Bar()async 方法怎么办?
    • @Lukas FluentAssertions 对此案例提供了很好的支持Func&lt;Task&gt; act = () =&gt; foo.BarAsync(); await act.Should().ThrowAsync&lt;SomeException&gt;();
    • 在这种情况下不要忘记将 lambda 函数标记为异步 (... async () =&gt; ...)。
    【解决方案2】:

    我可能误读了您的意图,但据我所知,没有必要对模拟做任何事情来测试是否引发了异常。

    看起来你有一个类,它的方法 Foo 接受一个字符串 - 让我们称之为 InnerClass

    public class InnerClass {
        public virtual void Foo(string str) {
             // do something with the string
        }
    }
    

    和一个包含 InnerClass 作为属性 (someProperty) 的类,它有一个成员 Koko,它以 List 作为参数

    public class OuterClass {
    
        private readonly InnerClass someProperty;
    
        public OuterClass(InnerClass someProperty) {
            this.someProperty = someProperty;
        }
    
        public void Koko(List<string> list) {
             foreach (var str in list) {
                  if (str != null)
                       someProperty.Foo(str);
                  else
                       throw new FormatException();
              }
        } 
    }
    

    注意:我无法让 List 编译 - 告诉我基础类型(字符串)必须是不可为空的。 AFAIK,只需要使值类型可以为空,引用类型可以隐式为空。

    您似乎想测试,如果您传入一个字符串列表,其中任何一个为空,则抛出 FormatException。

    如果是这样,那么 MOQ 的唯一原因就是让我们不必担心 InnerClass 功能。 Foo 是一种方法,因此,除非我们使用严格的模拟,否则我们可以只创建一个 InnerClass 模拟而无需其他设置。

    有一个属性[ExpectedException],我们可以用它标记我们的测试以验证是否抛出了异常。

    [TestMethod]
    [ExpectedException(typeof(FormatException))]
    public void ExceptionThrown() {
    
        var list = new List<string>() {
            "Abel",
            "Baker",
            null,
            "Charlie"
        };
    
        var outer = new OuterClass(new Mock<InnerClass>().Object);
        outer.Koko(list);
    
    }
    

    如果抛出 FormatException,则此测试将通过,否则将失败。

    【讨论】:

    • 我不知道 ExpectedException 属性,这很棒!谢谢。
    • 这才是真正的答案。谢谢
    【解决方案3】:

    阅读这些答案后,我意识到还有另一种方法可以使用 NUnit 来做到这一点。下面从异常中获取异常文本并验证错误消息文本。

    var ex = Assert.Throws<SomeException>(() => foo.Bar());
    Assert.That(ex.Message, Is.EqualTo("Expected exception text");
    

    而且我无法使用最新版本的 NUnit 使装饰/属性语法正常工作(上面 AlanT 的回答)——不知道为什么,但无论我尝试做什么,它都会抱怨。

    【讨论】:

      【解决方案4】:

      您可以使用 NUnit Asserts 测试是否引发了异常:

      Assert.That(() => testObject.methodToTest(), Throws.TypeOf<FaultException>());
      

      【讨论】:

        【解决方案5】:

        请阅读此Introduction to Moq。这是在调用DoSomething 方法时设置InvalidOperationException 抛出的方法:

        mock.Setup(foo => foo.DoSomething()).Throws<InvalidOperationException>();
        

        然后简单地验证方法是否被调用。如果它被调用,则引发异常

        mock.Verify(foo => foo.DoSomething());
        

        【讨论】:

        • 我不想设置异常。我想验证它确实被抛出了。
        • @GZ 您正在尝试验证最小起订量是否有效?如果您设置异常抛出,如果方法被调用,它将被抛出。只需验证方法是否被调用。
        • 我会更详细地解释这个场景。也许我的方法是不正确的。我有一个在两个对象上运行的 foreach 循环。一个可用,一个为空。我想验证两件事,第一个方法在可用对象上运行,并且为 null 对象抛出异常(在方法中我检查 null,如果是,则手动抛出异常)。
        • 你在这里嘲笑什么?很高兴看到你的代码。
        • @GZ 我仍然没有看到任何模拟。顺便说一句,您知道字符串可以为空吗?
        【解决方案6】:

        一个老问题,但没有源代码实际显示解决方案是什么,所以这就是我所做的:

        var correctExceptionThrown = false;
        
        try
        {
            _myClass.DoSomething(x);
        }
        catch (Exception ex)
        {
            if (ex.Message == "Expected message")
                correctExceptionThrown = true;
        }                    
        
        Assert.IsTrue(correctExceptionThrown);
        

        请注意,您可以捕获特定类型的异常(通常更可取),而不是检查消息。

        【讨论】:

          【解决方案7】:

          好的,我通过以下方式解决了它。

          由于异常破坏了我的测试,我将方法调用放在 try-catch 中的因为块中。

          然后我可以使用简单的验证。

          感谢所有帮助者...

          【讨论】:

          • 考虑发布您的解决方案代码,因为其他人可以从中学习或评论。您也可以将此答案标记为解决方案。
          • 听起来 AlanT 有正确的答案。在最小起订量中,如果你想测试 Foo(string) 将抛出 ArgumentException 你应该使用 ExpectedException
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-11
          • 2016-05-05
          • 1970-01-01
          • 1970-01-01
          • 2015-04-03
          • 1970-01-01
          相关资源
          最近更新 更多