Как шпионить за тестируемым классом с AutofacContrib.NSubstitute
Я запускаю модульные тесты в проекте библиотеки классов с платформой NSpec, AutofacContrib.NSubstitute v3.3.2.0, NSubstitute v1.7.0.0 (последняя версия на данный момент - 1.8.2).
Экземпляр класса при тестировании построен с AutoSubstitute
для того, чтобы автоматически блокировать все необходимые зависимости.
AutoSubstitute autoSubstitute = new AutoSubstitute();
MainPanelViewModel viewModel = autoSubstitute.Resolve<MainPanelViewModel>();
При правильной работе мой тестируемый класс в какой-то момент вызовет один из методов базового класса с каким-то конкретным входным параметром (базовый класс находится вне моего контроля):
// ...
base.ActivateItem(nextScreen);
// ...
Итак, для ожидания теста мне нужно проверить (шпион), что экземпляр вызывает базовый метод:
viewModel.Received().ActivateItem(Arg.Any<SomeSpecificScreenType>());
Вот проблема: когда я пытаюсь сделать это, во время выполнения NSubstitute жалуется, что я могу только запустить Received()
против объекта, созданного с Substitute.For<>()
, Я также быстро проверил исходный код AutofacContrib.NSubstitute, но я не смог найти способ получить экземпляр с помощью автоматической блокировки и в то же время обернуть его каким-либо образом в шпионский объект или что-то в этом роде.
Я также подумал, что, возможно, Substitute.ForPartsOf<>()
может быть полезным, но этот метод, кажется, не найден в NSubstitute v1.7.0.
Для полноты, вот полная ошибка NSubstitute:
Методы расширения NSubstitute, такие как.Received(), могут быть вызваны только для объектов, созданных с использованием Substitute.For() и связанных методов.
2 ответа
Для полноты я провел некоторые эксперименты с частичными заменами NSubstitute с ForPartsOf
,
Путь ForPartsOf
работает, по сути, для определения нового класса, который наследуется от класса, который вы используете в качестве шаблона. Это ограничивает то, что фреймворк может перехватывать методами, которые либо определены как abstract
или же virtual
, Это то же самое ограничение, которое вы имели бы для изменения поведения класса, если бы вы унаследовали его от своего собственного класса.
Принимая эту информацию, давайте посмотрим на вашу проблему. Вы хотите перехватить этот звонок:
base.ActivateItem(nextScreen);
Таким образом, из-за указанных выше ограничений, вы сможете перехватить вызов ActivateItem
, метод должен быть помечен как virtual
в базовом классе. Если это не так, вы ничего не можете сделать без изменения структуры приложения.
Если метод помечен как virtual
затем вы можете перехватить его с помощью NSubstitute, но вы можете сделать это только в том случае, если вызывается реализация NSubstitute. Это работает через обычную диспетчеризацию метода, потому что реализация виртуального метода самого высокого уровня вызывается (тот, который предоставляется NSubstitute), когда вы вызываете его. Однако это не работает, когда вы вызываете метод через base
ссылка.
Итак, пока вы можете перехватить это:
ActivateItem(nextScreen)
Вы просто не можете перехватить это:
base.ActivateItem(nextScreen);
Тот факт, что вы используете base.ActivateItem
в вашем коде предполагается, что у вашего тестируемого класса есть своя собственная реализация метода, который вы не хотите вызывать, поэтому с вашими текущими инструментами вы не сможете достичь того, что пытались сделать. Вот почему хорошо, что вы нашли обходной путь.
Вы находитесь в такой же ситуации с большинством других фальшивых фреймворков, включая Moq. Исключением является TypeMock, который использует совершенно другой способ перехвата вызовов методов, что означает, что он может делать то, что другие платформы просто не могут.
Таким образом, актуальная проблема не была действительно решена: просто проблема сама по себе исчезла.
Чтобы проверить правильность поведения, я вспомнил, что мог бы также прибегнуть к ActiveItem
публичная собственность из базового класса, поэтому я перестал использовать Receive()
и вернулся к простому сравнению значений.
Тем не менее, для дальнейшего использования, я не нашел способа шпионить за тестируемым классом с помощью этих библиотек. Я знаю, что следует избегать шпионажа в тестируемом классе, но, как и во многих других случаях, иногда вам нужно это делать.
НТН