Reset mock verification in Moq?

前端 未结 8 1648
执笔经年
执笔经年 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:40

    I don't think you can reset a mock like this. Instead, if you know that Fizz should be called once when transitioning to state 1, you can do your verifies like this:

    objectUnderTest.DoStuffToPushIntoState1();
    foo.Verify(x => x.Fizz(), Times.Once());  // or however many times you expect it to be called
    
    objectUnderTest.DoStuffToPushIntoState2();
    foo.Verify(x => x.Fizz(), Times.Once());
    

    Having said that, I would still create two separate tests for this. As two tests, it's easier to see whether the transition into state 1 is failing, or the transition into state 2 is failing. Additionally, when tested together like this, if your transition into state 1 fails, the test method exits and your transition into state 2 doesn't get tested.

    Edit

    As an example of this, I tested the following code with xUnit:

    [Fact]
    public void Test()
    {
        var foo = new Mock<IFoo>(MockBehavior.Loose);
    
        foo.Object.Fizz();
        foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");
    
        // stuff here
        foo.Object.Fizz();
        foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2"); 
    }
    

    This test fails with the message, "Failed after State 2". This simulates what would happen if your method that pushes foo into State 2 calls Fizz. If it does, the second Verify will fail.

    Looking at your code again, since you are calling one method to verify it does/does not call another method on the mock, I think you need to set CallBase to true so that the base DoStuffToPushIntoState2 is called rather than the mock's override.

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

    I think long after this post was created they added the functionality that the OP had asked for, there is a Moq extension method called Moq.MockExtensions.ResetCalls().

    With this method you can do exactly what you wished as shown below:

    [Test]
    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());
    
        foo.ResetCalls(); // *** Reset the verification here with this glorious method ***
    
        objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
    
        foo.Verify(x => x.Fizz(), Times.Never());
    }
    

    Update

    Now instead of .ResetCalls() we should use .Invocations.Clear() on the latest version of the library:

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