如何使用Moq测试方法调用顺序

目前我有:

[Test] public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() { // Arrange. var screenMockOne = new Mock(); var screenMockTwo = new Mock(); var screens = new List(); screens.Add(screenMockOne.Object); screens.Add(screenMockTwo.Object); var stackOfScreensMock = new Mock(); stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray()); var screenManager = new ScreenManager(stackOfScreensMock.Object); // Act. screenManager.Draw(new Mock().Object); // Assert. screenMockOne.Verify(smo => smo.Draw(It.IsAny()), Times.Once(), "Draw was not called on screen mock one"); screenMockTwo.Verify(smo => smo.Draw(It.IsAny()), Times.Once(), "Draw was not called on screen mock two"); } 

但是我在生产代码中绘制对象的顺序并不重要。 我可以先做一个,或者两个没关系。 然而,它应该很重要,因为抽奖顺序很重要。

你如何(使用Moq)确保按特定顺序调用方法?

编辑

我摆脱了那个考验。 draw方法已从我的unit testing中删除。 我只需要手动测试它的工作原理。 顺序的颠倒虽然被带入了一个单独的测试类,在那里进行了测试,所以并非一切都不好。

感谢有关他们正在研究的function的链接。 我当然希望它能很快得到补充,非常方便。

它似乎目前尚未实施。 参见问题24:MockSequence 。 该主题讨论了该问题。

不过,您可能会考虑修改测试。 我一般认为测试顺序导致脆弱的测试,因为它经常测试实现细节。

编辑:我不确定这是否解决了OP的问题。 Lucero的回答可能更有帮助。

我最近创建了Moq.Sequences,它提供了检查Moq中的排序的能力。 您可能想阅读描述以下内容的post :

  • 支持方法调用,属性设置器和getter。
  • 允许您指定应该预期特定呼叫的次数。
  • 提供循环,允许您将呼叫分组到定期组。
  • 允许您指定循环的预期次数。
  • 预期按顺序调用的调用可以与任何顺序中预期的调用混合。
  • multithreading支持。

典型用法如下:

 [Test] public void Should_show_each_post_with_most_recent_first_using_sequences() { var olderPost = new Post { DateTime = new DateTime(2010, 1, 1) }; var newerPost = new Post { DateTime = new DateTime(2010, 1, 2) }; var posts = new List { newerPost, olderPost }; var mockView = new Mock(); using (Sequence.Create()) { mockView.Setup(v => v.ShowPost(newerPost)).InSequence(); mockView.Setup(v => v.ShowPost(olderPost)).InSequence(); new BlogPresenter(mockView.Object).Show(posts); } } 

使用Moq CallBacks的简单解决方案:

  [TestMethod] public void CallInOrder() { // Arrange string callOrder = ""; var service = new Mock(); service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder += "1"); service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder += "2"); var sut = new Client(service); // Act sut.DoStuff(); // Assert Assert.AreEqual("12", callOrder); } 

看看这篇博文 ,它可能会解决您的问题。

否则,您可以使用Callback函数并增加/存储callIndex值。

从原始post我可以假设测试方法执行以下操作调用:

 var screenOne = new Screen(...); var screenTwo = new Screen(...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); screenManager.Draw(); 

‘Draw’方法的实现是这样的:

 public class ScreenManager { public void Draw() { _screens[0].Draw(); _screens[1].Draw(); } } 

从我的角度来看,如果调用顺序非常重要,那么应该在系统中引入附加结构(描述序列)。

最简单的实现:每个屏幕应该知道他的后续元素并在绘制自己后调用其Draw方法:

 // 1st version public class Screen(Screen screenSubSequent) { private Screen _screenNext; public Screen(Screen screenNext) { _screenNext=screenNext; } public void Draw() { // draw himself if ( _screenNext!=null ) _screenNext.Draw(); } } public class ScreenManager { public void Draw() { _screens[0].Draw(); } } static void Main() { var screenOne = new Screen(null, ...); var screenTwo = new Screen(screenOne, ...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); } 

从一点来看,每个Screen元素应该对另一个元素有所了解。 这并不总是好的。 如果是这样的话:你可以像’ScreenDrawer’那样创建一些类。 这个对象将存储自己的屏幕和后续屏幕(可能从Screen类inheritance他。使用其他世界:’ScreenDrawer’类描述系统结构。这是最简单的实现方案:

 // 2nd version public class ScreenDrawer { private Screen _screenNext; public ScreenDrawer(Screen screenNext, ...) : base (...) { _screenNext=screenNext; } public void Draw() { // draw himself if ( _screenNext!=null ) _screenNext.Draw(); } } public class ScreenManager { public void Draw() { _screens[0].Draw(); } } static void Main() { var screenOne = new ScreenDrawer(null, ...); var screenTwo = new ScreenDrawer(screenOne, ...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); } 

第二种方法引入了额外的inheritance,但不需要Screen类来了解他的子序列元素。

总结:两种方法都执行子序列调用,不需要“序列”测试。 相反,他们需要测试当前的“屏幕”是否调用另一个并测试’ScreenManager’是否依次调用第一个元素的’Draw’方法。

这种方法:

  1. 更可测试(可以使用大多数测试框架实现,而无需支持’序列测试’);
  2. 更稳定(没有人可以轻松更改序列:hi不仅需要更新源代码,还需要更新一些测试);
  3. 更面向对象(你使用的是对象,而不是像’sequence’这样的抽象实体);
  4. 结果:更可支持。

谢谢。