模拟方法执行时间和顺序

Mocking Method Execution Times and Sequence

我将 Moq 与方法接口结合使用。我需要测试这个接口中的方法是否按照一定的顺序执行,并且每个方法执行一定的次数。

界面

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. 如何将两者结合起来,以便我可以测试执行次数和正确的顺序?

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

  var mock = Substitute.For<IInterface>();
  var obj = new MyClass();
  await obj.Run();

  Received.InOrder(() => {
    mock.MethodOne("foo");
    mock.MethodTwo("foo");
    });

您对 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();