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

发布于 2024-08-12 01:22:09 字数 1261 浏览 4 评论 0 原文

目前我有:

    [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");
    }

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

您如何(使用起订量)确保按特定顺序调用方法?

编辑

我摆脱了那个测试。绘制方法已从我的单元测试中删除。我只需要手动测试它是否有效。不过,顺序的颠倒被纳入一个单独的测试类中进行了测试,所以这并不全是坏事。

感谢您提供有关他们正在研究的功能的链接。我当然希望它很快就会被添加,非常方便。

At the moment I have:

    [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");
    }

But the order in which I draw my objects in the production code does not matter. I could do one first, or two it doesn't matter. However it should matter as the draw order is important.

How do you (using Moq) ensure methods are called in a certain order?

Edit

I got rid of that test. The draw method has been removed from my unit tests. I'll just have to manually test it works. The reversing of the order though was taken into a seperate test class where it was tested so it's not all bad.

Thanks for the link about the feature they are looking into. I sure hope it gets added soon, very handy.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

乱了心跳 2024-08-19 01:22:09

我最近创建了 Moq.Sequences,它提供了在 Moq 中检查订购的功能。您可能想阅读我的帖子,其中描述了以下内容:

  • 支持方法调用,财产
    setter 和 getter。
  • 允许您指定数量
    一次特定的呼叫应该是
    预期的。
  • 提供循环,允许您
    将呼叫分组到循环组中。
  • 允许您指定数量
    预计会出现循环次数。
  • 预计被呼叫的呼叫
    按顺序可以相互混合
    预计以任何顺序进行的呼叫。
  • 多线程支持。

典型用法如下:

[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);
    }
}

I recently created Moq.Sequences which provides the ability to check ordering in Moq. You may want to read my post that describes the following:

  • Supports method invocations, property
    setters and getters.
  • Allows you to specify the number of
    times a specific call should be
    expected.
  • Provides loops which allow you to
    group calls into a recurring group.
  • Allows you to specify the the number
    of times a loop should be expected.
  • Calls that are expected to be called
    in sequence can be inter-mixed with
    calls that are expected in any order.
  • Multi-threaded support.

Typical usage looks like:

[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);
    }
}
原来是傀儡 2024-08-19 01:22:09

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

    [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);
    }

A simple solution using Moq CallBacks:

    [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);
    }
鹊巢 2024-08-19 01:22:09

目前看来还没有实施。请参阅问题 24:MockSequence此帖子讨论了该问题。

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

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

It appears that it's not currently implemented. See Issue 24: MockSequence. This thread discusses the issue.

You might consider revising your tests, though. I generally feel that testing order leads to fragile tests, as it's often testing implementation details.

EDIT: I'm not sure that this addresses the OP's question. Lucero's answer may be more helpful.

随遇而安 2024-08-19 01:22:09

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

Have a look at this blog post, it may solve your problem.

海未深 2024-08-19 01:22:09

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

Otherwise you could have used the Callback functions and increment/store a callIndex value.

柠北森屋 2024-08-19 01:22:09

从原来的帖子中,我可以假设测试方法执行以下操作调用:

var screenOne = new Screen(...);
var screenTwo = new Screen(...);
var screens = new []{screenOne, screenTwo};
var screenManager = new ScreenManager(screens);
screenManager.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);
}

从这一点开始,每个屏幕元素都应该了解另一点。这并不总是好的。如果是这样:您可以创建一些类,例如“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 类 摘要:这两种方法都执行顺序

调用,并且不需要“序列”测试,而是需要测试当前“屏幕”是否调用另一个屏幕并测试“ScreenManager”是否调用“绘制”方法。 更可测试(可以使用大多数测试框架来实现,

这种方法:

  1. 无需支持“序列测试”);
  2. 更稳定(没有人可以轻松更改序列:您不仅需要更新源代码,但也更新了一些测试);
  3. 更多面向对象(您正在使用对象,而不是像“序列”这样的抽象实体);
  4. 因此:更受支持

From the original post I could assume that the testing method do the following operations call:

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

Where 'Draw' method implementation is something like this:

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

From my perspective, if the call order is very important then additional structure (that describe sequence) should be introduced into system.

The simplest implementation: each screen should know his subsequent element and call its Draw method after drawing himself:

// 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);
}

From the one point, each Screen element should know a little about another one. This is not always good. If so: you can create some class like 'ScreenDrawer'. This object will store own screen and subsequent screen (probably inherit him from Screen class. Using other worlds: 'ScreenDrawer' class describes system structure. Here is a simplest scenario of implementation:

// 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);
}

2nd method introduce additional inheritance, but doesn't required Screen class to know about his subsequence element.

Summary: both methods do sub-sequential calls and doesn't require 'sequence' testing. Instead they require testing if current 'screen' calls another one and testing if 'ScreenManager' calls 'Draw' method of the 1st element in sequence.

This approach:

  1. More testable (can be implemented using most of testing framework without necessity to support 'sequence testing');
  2. More stable (nobody can easily change a sequence: hi will need not only update the source code, but also update few tests);
  3. More object oriented (you are working with object, not with abstract entities like 'sequence');
  4. As a result: much more supportable.

Thanks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文