【问题标题】:Mocking Method Execution Times and Sequence模拟方法执行时间和顺序
【发布时间】:2015-08-13 15:13:22
【问题描述】:

我正在使用与方法接口配对的起订量。我需要测试这个接口中的方法是否按一定的顺序以及每个方法执行了一定的次数。

界面

public interface IInterface
{
    void MethodOne(string foo);
    void MethodTwo(string foo);
}

方法

// MyClass stuff ...

public async Task Run()
{
    MethodOne("foo");
    MethodTwo("foo");
}

// ...

测试

我编写了这个测试来验证这些方法只执行了一定的次数(一次):

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.Verify(i=> i.MethodOne("foo"), Times.Once());
    mock.Verify(i=> i.MethodTwo("foo"), Times.Once());
}

这很好用...

我已经尝试过这些测试以确定是否正确满足了某个顺序,但测试似乎总是通过。

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
    mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));
}

应该通过,并且确实...

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
    mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
}

不应该通过,但是可以...

  1. 我需要做些什么来验证是否符合正确的顺序?
  2. 如何将两者结合起来,以便测试执行次数和正确的顺序?

【问题讨论】:

  • 我将此标记为重复,因为这两个问题似乎指的是同一个问题。如果他们不这样做,请告诉我,我会投票重新开放。
  • 我在问这个问题之前阅读了这个问题。该问题已通过回答说这是 Moq 中的错误得到解决。那是 3 年前的事了……我认为这仍然不是错误。
  • 我重新打开了这个问题

标签: c# .net moq


【解决方案1】:

您对InSequence 的示例测试似乎顺序错误。在调用Run 方法之前,您应该执行Setup。我假设这是一个错字,并且您的实际测试执行Setup,然后以正确的顺序调用该对象。也不清楚您对IInterface 的模拟如何到达MyClass。在我的最后一个例子中,我假设它实际上是被注入的......

Moq 的 InSequence 支持似乎仅在您使用严格的模拟时才有效。如果您像这样创建Mock

var mock = new Mock<IInterface>(MockBehavior.Strict);

那么这将起作用:

mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));

虽然失败:

mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));

请注意,出现的错误是该方法缺少相应的设置...而不是按顺序调用它,如果您没有预料到,这不一定是最容易解码的。

如果您不想/不能使用严格的模拟,另一种方法是使用回调实现您自己的序列检查。像这样的:

int sequence = 1;

mock.Setup(i => i.MethodOne("foo"))
    .Callback(()=>Assert.AreEqual(1, sequence++, "Method called out of sequence"));
mock.Setup(i => i.MethodTwo("foo"))
    .Callback(() => Assert.AreEqual(2, sequence++, "Method called out of sequence"));

var obj = new MyClass(mock.Object);
await obj.Run();

【讨论】:

    【解决方案2】:

    这可能比你想的更离题,但NSubstitute 是一个很好的模拟库,可以很好地处理这个问题。 在 NSubstitute 中它只是:

      var mock = Substitute.For<IInterface>();
      var obj = new MyClass();
      await obj.Run();
    
      Received.InOrder(() => {
        mock.MethodOne("foo");
        mock.MethodTwo("foo");
        });
    

    【讨论】:

    • 感谢您的建议。我来看看。这似乎比 Moq 有更好的文档。
    • 是的,而且 API 更好用。
    • 哇,你说得对。这好多了。直观得多。我会给它一些,看看是否有人可以回答更具体关于起订量的问题。如果没有,我给你。
    最近更新 更多