模拟方法执行时间和顺序

时间:2015-05-30 22:06:57

标签: c# .net moq

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

2 个答案:

答案 0 :(得分:8)

InSequence的示例测试似乎顺序错误。在调用Setup方法之前,您应该执行Run。我认为这是一个错字,你的实际测试是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();

答案 1 :(得分:4)

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

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

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