使用Moq来validation呼叫是以正确的顺序进行的

我需要testing下面的方法:

CreateOutput(IWriter writer) { writer.Write(type); writer.Write(id); writer.Write(sender); // many more Write()s... } 

我创build了一个Moq'd IWriter ,我想确保Write()方法以正确的顺序被调用。

我有以下testing代码:

 var mockWriter = new Mock<IWriter>(MockBehavior.Strict); var sequence = new MockSequence(); mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType)); mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId)); mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender)); 

但是,在CreateOutput() Write()的第二次调用(写入id值)会引发MockException ,消息“ IWriter.Write()调用失败,模拟行为Strict。模拟上的所有调用都必须具有相应的设置。 ”。

我也很难find任何确定的,最新的Moq序列的文档/例子。

我做错了什么,或者我不能使用相同的方法设置序列? 如果没有,是否有一个替代我可以使用(最好使用Moq / NUnit)?

在同一个模拟上使用MockSequence时有bug。 它肯定会在Moq库的更高版本中得到修复(您也可以通过更改Moq.MethodCall.Matches实现手动修复它)。

如果您只想使用Moq,则可以通过callbackvalidation方法调用顺序:

 int callOrder = 0; writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0))); writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1))); writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2))); 

我已经设法得到我想要的行为,但它需要从http://dpwhelan.com/blog/software-development/moq-sequences/下载第三方库;

然后可以使用以下方法testing序列:

 var mockWriter = new Mock<IWriter>(MockBehavior.Strict); using (Sequence.Create()) { mockWriter.Setup(x => x.Write(expectedType)).InSequence(); mockWriter.Setup(x => x.Write(expectedId)).InSequence(); mockWriter.Setup(x => x.Write(expectedSender)).InSequence(); } 

我已经添加了这个答案的一部分来帮助logging这个解决scheme,但是我仍然对单独使用Moq 4.0是否可以实现类似的function感兴趣。

我不确定Moq是否还在开发中,但是用MockSequence解决这个问题,或者在Moq中包含moq-sequences扩展,我们不难看出。

我写了一个扩展方法,将根据调用顺序进行断言。

 public static class MockExtensions { public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class { // All closures have the same instance of sharedCallCount var sharedCallCount = 0; for (var i = 0; i < expressions.Length; i++) { // Each closure has it's own instance of expectedCallCount var expectedCallCount = i; mock.Setup(expressions[i]).Callback( () => { Assert.AreEqual(expectedCallCount, sharedCallCount); sharedCallCount++; }); } } } 

它通过利用closures范围variables的方式来工作。 由于sharedCallCount只有一个声明,因此所有的闭包都会引用相同的variables。 通过expectedCallCount,一个新的实例在循环的每个迭代中被实例化(而不是简单地在闭包中使用i)。 这样,每个闭包都有一个范围的拷贝,只有在调用expression式的时候才能与sharedCallCount进行比较。

这是一个扩展的小unit testing。 请注意,此方法在您的设置部分中调用,而不是您的断言部分。

 [TestFixture] public class MockExtensionsTest { [TestCase] { // Setup var mock = new Mock<IAmAnInterface>(); mock.ExpectsInOrder( x => x.MyMethod("1"), x => x.MyMethod("2")); // Fake the object being called in order mock.Object.MyMethod("1"); mock.Object.MyMethod("2"); } [TestCase] { // Setup var mock = new Mock<IAmAnInterface>(); mock.ExpectsInOrder( x => x.MyMethod("1"), x => x.MyMethod("2")); // Fake the object being called out of order Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2")); } } public interface IAmAnInterface { void MyMethod(string param); } 

最近,我为Moq:VerifyInSequence()和VerifyNotInSequence()组合了两个特性。 他们甚至与松散的模拟工作。 但是,这些仅在moq存储库分叉中可用:

https://github.com/grzesiek-galezowski/moq4

等待更多的评论和testing,然后再决定是否可以纳入正式的moq发行版。 但是,没有任何东西可以阻止你将ZIP源文件下载到一个dll文件中,然后试一试。 使用这些function,您需要的序列validation可以这样写:

 var mockWriter = new Mock <IWriter>(){CallSequence = new LooseSequence()};

 //执行必要的调用

 mockWriter.VerifyInSequence(x => x.Write(expectedType));
 mockWriter.VerifyInSequence(x => x.Write(expectedId));
 mockWriter.VerifyInSequence(x => x.Write(expectedSender));

(注意,你可以使用另外两个序列,这取决于你的需要,松散的序列将允许你想要validation的序列之间的任何调用,StrictSequence不会允许这样做,StrictAnytimeSequence就像StrictSequence(validation调用之间没有方法调用),但允许该序列前面要有任意数量的任意调用。

如果您决定尝试使用此实验性function,请对您的想法发表评论: https : //github.com/Moq/moq4/issues/21

谢谢!

最简单的解决scheme将使用队列 :

 var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender}); mockWriter.Setup(x => x.Write(expectedType)) .Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s)); 

我怀疑这个预期是不是你所期望的。

不过,我可能只是编写我自己的IWriter实现来validation在这种情况下…可能更容易(稍后更容易更改)。

对不起,没有Moqbuild议直接。 我喜欢它,但没有做到这一点。

你可能需要添加.Verify()在每个安装结束? (这真的是一个猜测,虽然我很害怕)。