tags:

views:

245

answers:

5

In the following code why does mockTest.ToString() return Null?

EDIT: Added comment into example code to show how to fix the problem.

Public Sub Main()

    Try

        Dim test = New TestClass

        If test.ToString <> "stackoverflow rules" Then
            Throw New Exception("Real Failed: Actual value: <" + test.ToString + ">")
        End If

        Dim mock = New Moq.Mock(Of TestClass)()
        mock.SetupGet(Function(m As TestClass) m.Name).Returns("mock value")

        ' As per Mark's accepted answer this is the missing line of 
        ' of code to make the code work.
        ' mock.CallBase = True

        Dim mockTest = DirectCast(mock.Object, TestClass)

        If mockTest.ToString() <> "mock value" Then
            Throw New Exception("Mock Failed: Actual value: <" + mockTest.ToString + ">")
        End If

        Console.WriteLine("All tests passed.")

    Catch ex As Exception

        Console.ForegroundColor = ConsoleColor.Red
        Console.WriteLine(ex.ToString)
        Console.ForegroundColor = ConsoleColor.White

    End Try

    Console.WriteLine()
    Console.WriteLine("Finished!")
    Console.ReadKey()

End Sub

Public Class TestClass

    Public Sub New()
    End Sub

    Public Overridable ReadOnly Property Name() As String
        Get
            Return "stackoverflow rules"
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return Me.Name
    End Function

End Class
+1  A: 

Because you haven't told it to return anything else. You are relying on the internal workings of your ToString method to return the value of the name property, but the ToString method itself is being mocked.

I think you need to set the CallBase property of your mock to true to specify that unexpected method calls are actually executed on the base object.

David M
But he is doing a `mock.SetupGet(Function(m As TestClass) m.Name).Returns("mock value")` - shouldn't that do it?
Aviad P.
No, because he is relying on his `ToString` implementation to access the `Name` property, and this will not happen because the `ToString` method has been replaced with the one in the mock.
David M
+3  A: 

Both the Name property and the ToString method on TestClass are virtual/overridable, which means that Moq will mock them.

By default, Moq returns null for members with reference type return types, unless you explicitly tell it to return something else. Since a string is a reference type, it returns null.

You can fix it by setting CallBase to true.

Setting CallBase to true will cause Moq to call the base implementation if you do not explictly define an override:

mock.CallBase = True

In this case, this will instruct the mock to use the base implementation of ToString since no eplicit Setup exists (and thus invoke the Name property, which does have a Setup).

Mark Seemann
Thanks. Very well explained. Seems a strange default behaviour.
Tim Murphy
Yes, it trips me up as well sometimes...
Mark Seemann
A: 

The source of the issue is really that you are mocking what you are testing.

This is the most common cause of these types of issues. Mocks are designed to isolate what you are really trying to test, rather than what you are trying to test itself.

You should test this method directly and mock any dependencies it might have, rather than the object you are trying to test itself.

The only reason you should be using partial mocks (which is what setting CallBase allows you to do) is if the class you are working with is abstract and you want to test the functionality of an abstract class even though you have no implementation classes.

Anderson Imes
@Anderson. Sorry I don't think I understand your answer.The example code I gave was simplistic for the purposes of posting the question. The case that caused me to ask the question was I had a performance stopwatch class where the ToString method formatted the elapsed time for logging purposes. I need to check the formatting of the result based on various lengths of time.In this instance isn't a partial mock of elapsed time and setting CallBase to true the only why to test the ToString method?
Tim Murphy
In your case I would test a helper method that did the formatting that accepted a timespan as an input, rather than testing the class this way... mocking in this way is (imo) sort of overkill. I agree though that this is one of those rare cases where a partial mock makes at least a little sense. I disagree a little with the approach... I consider formatting to be a function of a view, however I understand this is not a universally held opinion.
Anderson Imes
A: 

The above mentioned solution works only as long as you are not trying to mock what the ToString() method returns. E.g. if I would use a stub for above mentioned class to test another class, I need to be able to specify, what it returns once ToString() is called. Unfortunately even after using

stub.Setup(s => s.ToString()).Returns("fakevalue")

Moq will still return its own ToString() override ("CastleProxies ....."). If on the other hand I set stub.CallBase = true;

it will not use my Setup either.

The solution I found is to not use ToString() alltogether, but to introduce a property e.g. Name, which my classes will now use instead of ToString() and that I can setup easily.

In order to preserve the functionality of ToString() I just use return Name; in there.

Sometimes it is easier to do things a little different than usual to save yourself a lot of headaches ;)

Thorsten Lorenz
The accepted answer does work.
Tim Murphy
A: 

My solution was to create a dummy interface that defines ToString, and to add an extension method that makes it easier to setup expecations on ToString:

public static class MoqExtensions
{
    public static ISetup<IToStringable, string> SetupToString<TMock>(this Mock<TMock> mock) where TMock : class
    {
        return mock.As<IToStringable>().Setup(m => m.ToString());
    }

    //Our dummy nested interface.
    public interface IToStringable
    {
        /// <summary>
        /// ToString.
        /// </summary>
        /// <returns></returns>
        string ToString();
    }
}

You can then use the extension method like so:

[Test]
public void ExpectationOnToStringIsMet()
{
    var widget = new Mock<IWidget>();
    widget.SetupToString().Returns("My value").Verifiable();

    Assert.That(widget.Object.ToString(), Is.EqualTo("My value"));

    widget.Verify();
}
DanMan