【问题标题】:MOQ'ing method call sequenceMOQ'ing 方法调用顺序
【发布时间】:2014-07-27 21:10:14
【问题描述】:

我正在模拟我们用于某些企业库调用的包装器。世界上一切都很好,我的测试通过了,实际的代码工作了!

不过,我随后扩展了功能,并验证了所有测试仍然通过。宾果 - 他们做到了。然而,他们并没有在现实世界中,因为人们期望

InitialiseDBCommand(string, commandtype)

之前会被调用

AddCmdParameter(string, dbtype, object)

所以像一个好孩子一样,我想做的第一件事就是编写一个测试来展示这种行为并期望抛出异常。

如果 InitialDBCommand 没有被任何字符串调用,我需要安装 AddCmmParater 以引发异常。

我想我可以通过回调来做到这一点,但感觉应该有一个方法调用序列(而不是方法返回序列)。

类似

iDataAccessHelper.Setup(s=>s.AddCmdOutputParameter(It.IsAny<string>(), 
                  It.IsAny<DbType>(), 
                  It.IsAny<int>()))
              .When(w=>w.InitialiseDBCommand(etc etc)
              .Throws<NullReferenceException>()

任何指针?

【问题讨论】:

  • 您实际测试的是什么?哪种方法? AddCmdParameter ?您应该将此场景编写为单元测试,而不是复杂的模拟场景。
  • @DimitarDimitrov 简单地说,我相信 OP 想要一个测试来证明 InitialiseDBCommand 在 AddCmdParameter 之前被调用
  • 我正在测试一种名为“WriteBlob”的方法,该方法使用数据访问层。数据访问层包含这 2 个调用,它们似乎具有顺序依赖性。我可以看到我们在做什么 - 对此的测试需要更接近数据访问层代码本身。
  • 有人问过herehere
  • 它们似乎更多地与对同一方法的多次调用相关,每个调用都返回不同的值。现在在 Moq 内的支持。

标签: c# unit-testing moq


【解决方案1】:

有一个扩展 Moq 以添加此功能的库,称为 Moq Sequences。从他们的例子中看起来像

using (Sequence.Create())
{
    mock.Setup(_ => _.Method1()).InSequence();
    mock.Setup(_ => _.Method2()).InSequence(Times.AtMostOnce());
    ...
    // Logic that triggers the above method calls should be done here.
    ...

}";

它断言 Method1 在 Method2 之前被调用

【讨论】:

  • 这听起来像我一直在寻找的......他们为什么不将它纳入实际起订量!
【解决方案2】:

如果您只是想测试是否以正确的顺序调用了 2 个方法,一种方法是添加一个计数器,每次调用其中一个方法时递增一个计数器,然后检查它是否是正确的数字. Here 是描述它的一个答案。 这是我为您的场景编写的方法:

[Test]
public void TestingCallOrder() {
    int counter = 0;
    var mockDataAccessStuff = new Mock<IDataAccessStuff>();
    mockDataAccessStuff.Setup(x => x.AddCmdParameter(It.IsAny<string>())).Callback(() => {
        Assert.AreEqual(counter, 0);
        counter++;
    });

    mockDataAccessStuff.Setup(x => x.InitialiseDbCommand(It.IsAny<string>())).Callback(() => {
        Assert.AreEqual(counter, 1);
        counter++;
    });

    // more of the same
    var myClass = new ClassThatImTesting(mockDataAccessStuff.Object);
    myClass.DoWork();

    // make sure both methods are called at least once ...
    mockDataAccessStuff.Verify(x => x.AddCmdParameter(It.IsAny<string>()), Times.Once());
    mockDataAccessStuff.Verify(x => x.InitialiseDbCommand(It.IsAny<string>()), Times.Once());
}

我认为这是编写测试的一种非常简洁易读的方式。

【讨论】:

  • 这基本上是我开始走的路线。谢谢。
  • 这是一种巧妙的方法,但我会在其中添加一个额外的断言来检查方法是否被调用。目前,如果两者都没有被调用,则测试将通过,它可能不应该。
  • @AndyNichols 是的,同意!添加了 2 个额外的断言
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 2012-03-15
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
  • 2015-12-30
相关资源
最近更新 更多