How to test method call order with Moq

后端 未结 6 869
囚心锁ツ
囚心锁ツ 2020-11-30 10:43

At the moment I have:

    [Test]
    public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() {
        // Arrange.
        var screenMockOne = new Mock&         


        
相关标签:
6条回答
  • 2020-11-30 11:18

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

    0 讨论(0)
  • 2020-11-30 11:20

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

    0 讨论(0)
  • 2020-11-30 11:29

    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.

    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2020-11-30 11:31

    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);
        }
    
    0 讨论(0)
  • 2020-11-30 11:42

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题