如何使用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’方法。
这种方法:
- 更可测试(可以使用大多数测试框架实现,而无需支持’序列测试’);
- 更稳定(没有人可以轻松更改序列:hi不仅需要更新源代码,还需要更新一些测试);
- 更面向对象(你使用的是对象,而不是像’sequence’这样的抽象实体);
- 结果:更可支持。
谢谢。
- C# – 将float转换为int …并根据余数更改int
- System.Data.SqlClient.SqlException:超时已过期
- 如何在npgsql中调用存储过程来获取游标数据
- 如何在C#中获取列表中新添加项的索引?
- ExecuteRegisteredAsyncTasks是否释放请求处理线程以服务其他请求?
- Moq – 使用在执行测试期间更改的参数validation调用
- Signalr / Hub未在IIS 7中加载,但在Visual Studio中正常工作
- ASP.NET MVC 6中MVC Controller和Web API Controller有什么区别?
- 如何在Windows Phone中使用XDocument解析OPML