English 中文(简体)
How to test method call order with Moq
原标题:

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.

最佳回答

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.

问题回答

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

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

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

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

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.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签