【问题标题】:How to make a mock throw an exception the first time and return a value the second one如何使模拟第一次抛出异常并第二次返回值
【发布时间】:2011-10-27 17:26:37
【问题描述】:

我使用 Moq 作为我的模拟框架,我需要测试一个类,当运行特定类型的异常时,它会继续尝试,直到执行完成后情况得到解决。

所以我需要的是类似于:

myMock = Mock<IFoo>();

myMock.Setup(m => m.Excecute()).Throws<SpecificException>();
myMock.Setup(m => m.Execute());

var classUnderTest = MyClass(myMock);
classUnderTest.DoSomething();

Assert.AreEqual(expected, classUnderTest.Result);

感谢您提供的任何帮助。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    这是一种方法,基于 Moq QuickStart 在每次调用时返回不同值的示例。

    var mock = new Mock<IFoo>();
    var calls = 0;
    mock.Setup(foo => foo.GetCountThing())
        .Returns(() => calls)
        .Callback(() =>
         {
            calls++;
            if (calls == 1)
            {
                throw new InvalidOperationException("foo");
            }
         });
    
    try
    {
        Console.WriteLine(mock.Object.GetCountThing());
    }
    catch (InvalidOperationException)
    {
        Console.WriteLine("Got exception");
    }
    
    Console.WriteLine(mock.Object.GetCountThing());
    

    如果方法返回 void,使用:

    var myMock = new Mock<IFoo>();
    bool firstTimeExecuteCalled = true;
    
    myMock.Setup(m => m.Execute())
          .Callback(() =>
           {
                if (firstTimeExecuteCalled)
                {
                    firstTimeExecuteCalled = false;
                    throw new SpecificException();
                }
           });
    
    try
    {
        myMock.Object.Execute();
    }
    catch (SpecificException)
    {
        // Would really want to call Assert.Throws instead of try..catch.
        Console.WriteLine("Got exception");
    }
    
    myMock.Object.Execute();
    Console.WriteLine("OK!");
    

    【讨论】:

    • 这看起来很有趣,但是,如果方法是无效的并且我在设置后没有得到“返回”方法怎么办?实际上,如果“GetCountThing”返回一个 int,它就可以工作,如果它返回其他东西,它就不会编译。
    • 至于如果它不是 int,请更改您从 Returns 行返回的内容。它不必是calls 的值。
    • 这个(相当复杂的)解决方案不再需要,因为 Moq 支持 SetupSequence 方法。请参阅下面的答案以获取示例
    【解决方案2】:

    https://github.com/Moq/moq4/wiki/Quickstart#miscellaneous

    设置成员返回不同的值/在顺序调用时抛出异常:

    var mock = new Mock<IFoo>();
    mock.SetupSequence(f => f.GetCount())
        .Returns(3)  // will be returned on 1st invocation
        .Returns(2)  // will be returned on 2nd invocation
        .Returns(1)  // will be returned on 3rd invocation
        .Returns(0)  // will be returned on 4th invocation
        .Throws(new InvalidOperationException());  // will be thrown on 5th invocation
    

    【讨论】:

    • 这是我正在寻找的答案,不敢相信它没有更多的选票:)
    【解决方案3】:

    为什么不实际编写自己的测试对象呢?如果它只是用于测试,例如:

    public class Mock : IFoo
    {
         private int _calls;
    
         public Mock()
         {
             _calls = 0;
         }
    
         public int Execute()
         {
             _calls++;
    
             if (_calls == 1)
                 throw new Exception();
    
             return value;
    
         }
    
    }
    

    【讨论】:

    • 在这种情况下我可以看到。不过,这是一个极端情况;大多数时候,我要么想要一个返回已知常量值的存根,要么想要一个能够验证给定方法是否被调用的模拟。 Moq 使得动态创建这两个变得非常容易。此外,如果您有一个包含 30 个成员的旧版界面,并且只需要其中一个成员来进行此测试,则 Moq 解决方案要短得多。
    猜你喜欢
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    相关资源
    最近更新 更多