Почему Moq не выполняет переопределенный метод ToString?

В следующем коде, почему делает mockTest. ToString () возвращают Пустой указатель?

Править: Добавленный комментарий в пример кода, чтобы показать, как решить проблему.

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
6
задан Tim Murphy 4 January 2010 в 17:22
поделиться

5 ответов

Как свойство Name, так и метод ToString на TestClass являются виртуальными/перестраиваемыми, что означает, что Moq будет насмехаться над ними.

По умолчанию, Moq возвращает ноль для членов с типом возврата ссылочного типа, если только вы явно не скажете ему возвращать что-то другое. Так как строка является типом ссылки, она возвращает ноль.

Вы можете исправить это, установив CallBase в true.

Установка CallBase в true приведет к тому, что Moq вызовет базовую реализацию, если вы явно не определите переопределение:

mock.CallBase = True

В этом случае, это даст указание подражателю использовать базовую реализацию ToString, так как никакой электронной установки не существует (и, таким образом, вызовет свойство Name, которое does имеет установку).

.
8
ответ дан 8 December 2019 в 14:43
поделиться

Потому что вы не сказали ему вернуть что-либо другое. Вы полагаетесь на внутреннюю работу вашего метода ToString, чтобы вернуть значение свойства имени, но сам метод ToString высмеивается.

Я думаю, что вам нужно установить свойство CallBase вашего высмеивания в true, чтобы указать, что неожиданные вызовы метода на самом деле выполняются на базовом объекте.

.
4
ответ дан 8 December 2019 в 14:43
поделиться

Источником вопроса действительно, что вы издеваются, что вы тестируете .

Это наиболее распространенная причина этих видов вопросов. Макеты предназначены для выделения того, что вы действительно пытаетесь проверить, а не то, что вы пытаетесь проверить себя.

Вы должны проверить этот метод непосредственно и высмеивать любые зависимостей , а не объект, который вы пытаетесь проверить сам.

Единственная причина, по которой вы должны использовать частичные издевательства (какая настройка Callbase позволяет вам делать) - это если класс, с которым вы работаете, является абстрактным, и вы хотите проверить функциональность абстрактного класса, даже если у вас нет реализации классы.

0
ответ дан 8 December 2019 в 14:43
поделиться

Вышеупомянутое решение работает только до тех пор, пока вы не пытаетесь имитировать то, что возвращает метод ToString (). Например. если бы я использовал заглушку для вышеупомянутого класса для тестирования другого класса, мне нужно было бы указать, что он возвращает после вызова ToString (). К сожалению, даже после использования

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

Moq все равно будет возвращать собственное переопределение ToString () ("CastleProxies .....") . Если, с другой стороны, я установлю stub.CallBase = true ;

, он также не будет использовать мою установку.

Я нашел решение не использовать ToString () полностью, а ввести свойство, например Имя, которое мои классы теперь будут использовать вместо ToString () и которое я могу легко настроить.

Чтобы сохранить функциональность ToString (), я просто использую return Name; там.

Иногда проще сделать что-то немного отличное от обычного, чтобы избавиться от головной боли;)

1
ответ дан 8 December 2019 в 14:43
поделиться

Мое решение заключалось в создании фиктивного интерфейса, определяющего ToString, и добавлении метода расширения, который упрощает настройку ожиданий для 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();
    }
}

Вы затем можно использовать метод расширения следующим образом:

[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();
}
3
ответ дан 8 December 2019 в 14:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: