Reset mock verification in Moq?

前端 未结 8 1647
执笔经年
执笔经年 2021-02-11 12:01

Setup as so:

public interface IFoo
{
    void Fizz();
}

[Test]
public void A()
{
    var foo = new Mock(MockBehavior.Loose);

    foo.Object.Fizz();         


        
相关标签:
8条回答
  • 2021-02-11 12:33

    This is indeed unit test abuse as you are verifying two things in one test. Your life would be much easier if you took the ObjectUnderTest initialisation out of the test and into a common setup method. Then your tests become much more readable and independant of each other.

    More than production code, test code should be optimized for readability and isolation. A test for one aspect of system's behavior should not affect other aspects. It really is much much easier to refactor common code into a setup method than to try to reset the mock objects.

    ObjectUnderTest _objectUnderTest;
    
    [Setup] //Gets run before each test
    public void Setup() {
        var foo = new Mock<IFoo>(); //moq by default creates loose mocks
        _objectUnderTest = new ObjectUnderTest(foo.Object);
    }
    [Test]
    public void DoStuffToPushIntoState1ShouldCallFizz() {
        _objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
    
        foo.Verify(x => x.Fizz());
    }
    [Test]
    public void DoStuffToPushIntoState2ShouldntCallFizz() {
    {
        objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
        foo.Verify(x => x.Fizz(), Times.Never());
    }
    
    0 讨论(0)
  • 2021-02-11 12:33

    I have also witnessed the Times.Exactly(1) verification failure across unit tests using MoQ, with a "was called 2 times" error message. I see this as a bug in MoQ, as I would expect clean mock states on every test run.

    My work around was to assign a new mock instance and test target in the test setup.

    private Mock<IEntityMapper> entityMapperMock;
    private OverdraftReportMapper target;
    
    [SetUp]
    public void TestSetUp()
    {
      entityMapperMock = new Mock<IEntityMapper>();
      target = new OverdraftReportMapper(entityMapperMock.Object);
    } 
    
    0 讨论(0)
  • 2021-02-11 12:34

    Depends on which version of Mock you use, I know for sure we can do this

    someMockObject.ResetCalls();
    
    0 讨论(0)
  • 2021-02-11 12:34

    You could use the Callback method instead of Verify, and count the calls.

    This is demonstrated on the Moq Quick Start page, thus:

    // returning different values on each invocation
    var mock = new Mock<IFoo>();
    var calls = 0;
    mock.Setup(foo => foo.GetCountThing())
        .Returns(() => calls)
        .Callback(() => calls++);
    // returns 0 on first invocation, 1 on the next, and so on
    Console.WriteLine(mock.Object.GetCountThing());
    
    0 讨论(0)
  • 2021-02-11 12:36

    Next approach works fine for me (using Moq.Sequence)

        public void Justification()
        {
            var foo = new Mock<IFoo>(MockBehavior.Loose);
            foo.Setup(x => x.Fizz());
    
            var objectUnderTest = new ObjectUnderTest(foo.Object);
    
            objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
    
            foo.Verify(x => x.Fizz());
    
            // Some cool stuff
    
            using (Sequence.Create())
            {
                foo.Setup(x => x.Fizz()).InSequence(Times.Never())
                objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
            }
        }
    

    Let me know if it worked out for you

    0 讨论(0)
  • 2021-02-11 12:40

    Addition to answer by @stackunderflow (haha, nice nick :))

    In later versions of Moq, Moq.MockExtensions.ResetCalls() was marked as obsolete. mock.Invocations.Clear() should be used instead:

    foo.Invocations.Clear();
    
    0 讨论(0)
提交回复
热议问题