Verifying a method was called

30,442

Solution 1

The test fails when Hello() is internal because Moq cannot provide an override of the method in this case. This means that the internal implementation of Hello() will run, rather than mock's version, causing the Verify() to fail.

Incidentally, what you are doing here makes no sense in the context of a unit test. A unit test should not care that Say() calls an internal Hello() method. This is implementation internal to your assembly and not a concern of consuming code.

Solution 2

I don't know enough about how this works underneath the covers to give you a technical answer as to exactly why that is the behaviour, but I think I can help with your confusion.

In your example you are calling a method Say(), and it is returning the expected text. Your expectation should not enforce a particular implementation of Say() ie that it calls an internal method called Hello() to return that string. This is why it does not pass the verify, and also why the string returned is "", ie the actual implementation of Hello() has been called.

By making the Hello method public it appears that this has enabled Moq to intercept the call to it, and use it's implementation instead. Therefore, in this case the test appears to pass. However, in this scenario you haven't really achieved anything useful, because your test says that when you call Say() the result is "Hello World" when in acutal fact the result is "".

I have rewritten your example to show how I would expect Moq to be used (not necessarily definitive, but hopefully clear.

public interface IHelloProvider
{
    string Hello();
}

public class TestClass
{
    private readonly IHelloProvider _provider;

    public TestClass(IHelloProvider provider)
    {
        _provider = provider;
    }

    public string Say()
    {
        return _provider.Hello();
    }
}

[TestMethod]
public void WhenSayCallsHelloProviderAndReturnsResult()
{
    //Given
    Mock<IHelloProvider> mock = new Mock<IHelloProvider>();
    TestClass concrete = new TestClass(mock.Object);
    //Expect
    mock.Setup(x => x.Hello()).Returns("Hello World");
    //When
    string result = concrete.Say();
    //Then
    mock.Verify(x => x.Hello(), Times.Exactly(1));
    Assert.AreEqual("Hello World", result);
}

In my example, I have introduced an interface to an IHelloProvider. You will notice that there is no implementation of IHelloProvider. This is at the heart of what we are trying to achieve by using a mocking solution.

We are trying to test a class (TestClass), which is dependant on something external (IHelloProvider). If you are using Test Driven Development then you probably haven't written an IHelloProvider yet, but you are aware that you will need one at some point. You want to get TestClass working first though, rather than get distracted. Or perhaps IHelloProvider uses a database, or a flat file, or is difficult to configure.

Even if you have a fully working IHelloProvider, you are still only trying to test the behaviour of TestClass, so using a concrete HelloProvider is likely to make your test more prone to failure, for instance if there is a change to the behaviour of HelloProvider, you don't want to have to change the tests of every class that uses it, you just want to change the HelloProvider tests.

Getting back to the code, we now have a class TestClass, which is dependant on an interface IHelloProvider, the implementation of which is provided at construction time (this is dependency injection).

The behaviour of Say() is that it calls the method Hello() on the IHelloProvider.

If you look back at the test, we have created an actual TestClass object, since we actually want to test the code that we have written. We have created a Mock IHelloProvider, and said that we expect it to have it's Hello() method called, and when it does to return the string "Hello World".

We then call Say(), and verify the results as before.

The important thing to realise is that we are not interested in the behaviour of the IHelloProvider, and so we can mock it to make testing easier. We are interested in the behaviour of TestClass, so we have created an actual TestClass not a Mock, so that we can test it's actual behaviour.

I hope this has helped to clarify what is going on.

Solution 3

Moq doesn't do partial mocking and can only mock public virtual methods or interfaces. When you create a Mock you are creating a completely new T and removing all implementation of any public virtual methods.

My suggestion would be to concentrate on testing the public surface area of your objects and not their internals. Your internals will get coverage. Just make sure you have a clear idea of what your target class is and don't mock that (in most cases).

Partial mocking is only useful when you want to test the functionality of an abstract class with mocked implementations of abstract methods. If you aren't doing this, you are likely not going to see much benefit from doing a partial mock.

If this is not an abstract class, you will want to focus more on the approach Modan suggests, which is to say that you should mock dependencies rather than your target class itself. All of this boils down to the rule don't mock what you are testing.

Share:
30,442
jle
Author by

jle

Updated on July 27, 2022

Comments

  • jle
    jle almost 2 years

    Using Moq, I have a very odd issue where the setup on a mock only seems to work if the method I am setting up is public. I don't know if this is a Moq bug or if I just have this wrong (newbie to Moq). Here is the test case:

    public class TestClass
    {
        public string Say()
        {
            return Hello();
        }
    
        internal virtual string Hello()
        {
            return "";
        }
    }
    
    [TestMethod]
    public void Say_WhenPublic_CallsHello()
    {
        Mock<TestClass> mock = new Mock<TestClass>();
        mock.Setup(x => x.Hello()).Returns("Hello World");
    
        string result = mock.Object.Say();
        mock.Verify(x => x.Hello(), Times.Exactly(1));
        Assert.AreEqual("Hello World", result);     
    }
    

    Which fails with this message:

    Say_WhenPublic_CallsHello failed: Moq.MockException: Invocation was not performed on the mock 1 times: x => x.Hello() at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)...

    If I make the Hello method public like this, the test passes. What is the issue here?

    public virtual string Hello()
    {
        return "";
    }
    

    Thanks in advance!