C# 如何使用Moq测试方法调用顺序
目前我有:C# 如何使用Moq测试方法调用顺序,c#,unit-testing,moq,C#,Unit Testing,Moq,目前我有: [Test] public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() { // Arrange. var screenMockOne = new Mock<IScreen>(); var screenMockTwo = new Mock<IScreen>(); var screens = new List<IS
[Test]
public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() {
// Arrange.
var screenMockOne = new Mock<IScreen>();
var screenMockTwo = new Mock<IScreen>();
var screens = new List<IScreen>();
screens.Add(screenMockOne.Object);
screens.Add(screenMockTwo.Object);
var stackOfScreensMock = new Mock<IScreenStack>();
stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray());
var screenManager = new ScreenManager(stackOfScreensMock.Object);
// Act.
screenManager.Draw(new Mock<GameTime>().Object);
// Assert.
screenMockOne.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(),
"Draw was not called on screen mock one");
screenMockTwo.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(),
"Draw was not called on screen mock two");
}
[测试]
公共作废图纸所有屏幕显示在Stack()的上方{
//安排。
var screenMockOne=new Mock();
var screenMockTwo=新模拟();
var screens=新列表();
screens.Add(screenMockOne.Object);
screens.Add(screenMockTwo.Object);
var stackOfScreensMock=新Mock();
stackOfScreensMock.Setup(s=>s.ToArray()).Returns(screens.ToArray());
var screenManager=新的screenManager(stackOfScreensMock.Object);
//表演。
screenManager.Draw(新的Mock().Object);
//断言。
screenMockOne.Verify(smo=>smo.Draw(It.IsAny()),Times.Once(),
“未在屏幕上调用Draw模拟一”);
screenMockTwo.Verify(smo=>smo.Draw(It.IsAny()),Times.Once(),
“在模拟二号屏幕上未进行抽签”);
}
但我在生产代码中绘制对象的顺序并不重要。我可以先做一个或两个,没关系。但是,这很重要,因为提取顺序很重要
您(使用Moq)如何确保方法按特定顺序调用
编辑
我摆脱了那个测试。draw方法已从我的单元测试中删除。我只需要手动测试它的工作情况。相反的顺序被带到一个单独的测试类中,在那里它被测试过,所以它并不都是坏的
感谢您提供有关他们正在研究的功能的链接。我当然希望它很快被添加,非常方便。看看这个,它可能会解决您的问题。看起来它目前还没有实现。看见讨论这个问题 <>你可能会考虑修改你的测试。我通常认为测试顺序会导致脆弱的测试,因为它经常测试实现细节
编辑:我不确定这是否解决了OP的问题。Lucero的答案可能更有用。否则,您可以使用回调函数并递增/存储callIndex值。我最近创建了Moq.Sequences,它提供了在Moq中检查顺序的功能。您可能需要阅读我的,其中描述了以下内容:
- 支持方法调用、属性 二传手和接球手
- 允许您指定 特定通话的时间应为 预料之中
- 提供允许您执行以下操作的循环: 对定期组的组调用
- 允许您指定号码 循环次数应为预期次数
- 预期要调用的调用 按顺序可以相互混合 以任何顺序进行的呼叫
- 多线程支持
[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<Post> { newerPost, olderPost };
var mockView = new Mock<BlogView>();
using (Sequence.Create())
{
mockView.Setup(v => v.ShowPost(newerPost)).InSequence();
mockView.Setup(v => v.ShowPost(olderPost)).InSequence();
new BlogPresenter(mockView.Object).Show(posts);
}
}
[测试]
public void应使用序列()显示每个帖子和最近的帖子
{
var olderPost=newpost{DateTime=newdatetime(2010,1,1)};
var newerPost=newpost{DateTime=newdatetime(2010,1,2)};
var posts=新列表{newerPost,olderPost};
var mockView=new Mock();
使用(Sequence.Create())
{
mockView.Setup(v=>v.ShowPost(newerPost)).InSequence();
mockView.Setup(v=>v.ShowPost(olderPost)).InSequence();
新的BlogPresenter(mockView.Object).Show(posts);
}
}
从最初的帖子中,我可以假设测试方法执行以下操作调用:
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();
}
}
从我的角度来看,如果调用顺序非常重要,那么应该在系统中引入额外的结构(描述序列)
最简单的实现:每个屏幕都应该知道其后续元素,并在自己绘制后调用其绘制方法:
// 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);
}
从这一点来看,每个屏幕元素都应该对另一个屏幕元素有所了解。这并不总是好的。如果是这样:您可以创建一些类,如“ScreenDrawer”。此对象将存储自己的屏幕和后续屏幕(可能从screen类继承他)。使用其他世界:“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);
}
第二个方法引入了额外的继承,但不要求Screen类了解其子序列元素
总结:这两种方法都进行子顺序调用,不需要“顺序”测试。相反,它们需要测试当前“屏幕”是否调用另一个,以及“屏幕管理器”是否调用顺序中第一个元素的“绘图”方法
这种方法:
谢谢。使用Moq回调的简单解决方案:
[TestMethod]
public void CallInOrder()
{
// Arrange
string callOrder = "";
var service = new Mock<MyService>();
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);
}
[TestMethod]
公共无效Callinoder()
{
//安排
字符串callOrder=“”;
var service=newmock();
service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder+=“1”);
service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder+=“2”);
var sut=新客户(服务);
//表演
sut.DoStuff();
//断言
断言.AreEqual(“12”,callOrder);
}
在我的情况下无法实现此功能,我无法使用队列交换数组。不过现在没关系。请参阅编辑。断言/验证顺序可能会导致脆弱的测试,但这不是因为它正在测试实现细节。如果使用模拟,尤其是使用控制反转(依赖项注入)的类模式,您已经在测试实现细节;这就是重点。我要说的是,测试顺序不应该是测试中常见的模式,但在一些有效的情况下,没有其他方法可以驱动co