Как заменить Object.ToString с помощью NSubstitute?

Когда я пытаюсь использовать NSubstitute 1.7.1.0 для определения поведения объекта Object. ToString (виртуальный метод) NSubstitute генерирует исключение.

Воспроизвести:

[Test]
public static void ToString_CanBeSubstituted()
{
    var o = Substitute.For<object>();
    o.ToString().Returns("Hello world");

    Assert.AreEqual("Hello world", o.ToString());
}

Провал:

NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from.

Make sure you called Returns() after calling your substitute (for example: mySub.SomeMethod().Returns(value)),
and that you are not configuring other substitutes within Returns() (for example, avoid this: mySub.SomeMethod().Returns(ConfigOtherSub())).

If you substituted for a class rather than an interface, check that the call to your substitute was on a virtual/abstract member.
Return values cannot be configured for non-virtual/non-abstract members.

Correct use:
    mySub.SomeMethod().Returns(returnValue);

Potentially problematic use:
    mySub.SomeMethod().Returns(ConfigOtherSub());
Instead try:
    var returnValue = ConfigOtherSub();
    mySub.SomeMethod().Returns(returnValue);

Есть ли способ пройти вышеуказанный тест?

Исключение, выброшенное в результате моего наивного теста, является ошибкой или это «по замыслу»?


person Milan Gardian    schedule 14.02.2014    source источник


Ответы (2)


NSubstitute основан на библиотеке Castle.Core и использует динамические прокси для перехвата вызовов и управления ими. Перехват методов класса Object подавлен в обоих фреймворках.

  1. Он подавлен в реализации Castle по умолчанию IProxyGenerationHook. Вы можете найти код здесь. Думаю, на то есть причины. Конечно, можно реализовать собственный IProxyGenerationHook, который позволяет перехват методов класса Object, но ...

  2. NSubstitute также подавляет перехват методов Object, и причиной является синтаксис NSubstitute. «NSubstitute записывает вызовы, сделанные для замены, и когда мы вызываем Returns, он захватывает последний сделанный вызов и пытается настроить его для возврата определенного значения». Предположим, у нас есть следующий код:

    var service = Substitute.For<IService>();
    var substitute = Substitute.For<object>();
    service.AMethod(substitute).Returns(1);
    

    Мы вызываем здесь «AMethod ()», NSub перехватывает выполнение и делает свои внутренние вещи, предположим, добавляет «замещающее» значение в словарь, который вызывает substitute.GetHashCode (). Если бы мы перехватили метод GetHashCode (), то это был бы последний записанный сделанный вызов. NSub привяжет к нему указанное возвращаемое значение, что неверно. Избежать этого практически невозможно.

person Alexandr Nikitin    schedule 14.02.2014

У вас возникнут проблемы с заменой каких-либо методов из System.Object, так как он обрабатывается очень особым образом в .Net.

Например, это тоже не удается по той же причине:

[Test]
public void x()
{
    var o = Substitute.For<anything>();
    o.GetHashCode().Returns(3);
    Assert.AreEqual(4, o.GetHashCode());
}

Но это нормально работает:

public class Anything {
    public virtual string Else() {
        return "wrong";
    }
}

[Test]
public void x() {
    var o = Substitute.For<Anything>();
    o.Else().Returns("right");
    Assert.AreEqual("right", o.Else());
}

Извините, я не могу сообщить более хороших новостей, но насмешка над низкоуровневыми объектами не очень хорошо работает в .Net.

person Iain Ballard    schedule 14.02.2014
comment
Спасибо за ваш ответ, за усилия, которые вы вложили в свой ответ. Однако такое поведение больше похоже на надзор со стороны NSubstitute, а не на магию .NET (и если есть магия, я хотел бы узнать об этом больше ;-)). - person Milan Gardian; 14.02.2014
comment
К сожалению, это не недосмотр. Объект System.Object не является обычным классом .Net. Это корень сборщика мусора, идентификатор распределения в IL и неуправляемый COM-объект. Он очень глубоко похоронен в среде выполнения .Net. Я являюсь одним из участников проекта и могу сказать, что если вы углубитесь в глубины IL, все будет не так, как в C #. Удачи вам с NS-подстановкой! - person Iain Ballard; 14.02.2014