2015-06-25 5 views
1

Я запускаю модульные тесты в проекте библиотеки классов с NSpec framework, AutofacContrib.NSubstitute v3.3.2.0, NSubstitute v1.7.0.0 (последнее на данный момент 1,8,2).Как проверять класс под тестом с помощью AutofacContrib.NSubstitute

Класс испытания экземпляр построен с 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, но я не смог найти способ получить экземпляр с автопиляцией и в то же время wrap это как-то в объекте-шпионе или что-то в этом роде.

Я также подумал, что может быть полезным Substitute.ForPartsOf<>(), но этот метод, похоже, не найден в NSubstitute v1.7.0.

Для полноты картины, вот NSubstitute полная ошибка:

NSubstitute extension methods like .Received() can only be called on objects created using Substitute.For() and related methods.

+0

Вы действительно должны проверить, что имеет место поведение «ActivateItem» в базовом классе, а не проверка того, что сам метод вызван (что по сути является деталью реализации). Есть ли причина, по которой вы не можете этого сделать? 'Subsitute.ForPartsOf' * может * помочь, но, как правило, это плохая идея, чтобы издеваться над классом, который вы тестируете. Метод, который вы хотите протестировать, также должен быть виртуальным, поэтому, не тестируя его, я скептически отношусь к тому, что явный вызов 'base.ActivateItem' фактически вызовет замену в любом случае. Тестирование того, что делает метод, вероятно, будет проще. – forsvarir

+0

Я полностью согласен с этим в целом. Я собирался ответить на ваш комментарий, что это модель просмотра CaliburnMicro, и проверять поведение «ActivateItem» было бы трудно, учитывая, что мы заходим в область просмотра пользовательского интерфейса. Но потом я просто напомнил, что также должно быть свойство «ActiveItem» ... так что теперь я просто проверю, что вы здесь, – superjos

+0

! Теперь я задаюсь вопросом, стоит ли мне просто закрыть этот вопрос или что-то вроде – superjos

ответ

1

Для полноты, я сделал некоторые эксперименты с частичными заменами NSubstitute с ForPartsOf.

Поведение ForPartsOf по существу состоит в том, чтобы определить новый класс, который наследуется от класса, который вы используете в качестве шаблона. Это ограничивает то, что это значит, что издевательская структура может перехватывать методы, которые либо определены как abstract, либо virtual. Это то же ограничение, которое у вас было бы для изменения поведения класса, если вы должны наследовать его своим классом.

Используя эту информацию, давайте рассмотрим вашу проблему.Вы хотите, чтобы перехватить этот вызов:

base.ActivateItem(nextScreen); 

Таким образом, из-за ограничений выше, для вас, чтобы быть в состоянии перехватить вызов ActivateItem, метод должен быть отмечен как virtual в базовом классе. Если это не так, то есть ничего вы можете обойтись без изменения структуры приложения.

Если метод помечен как virtual, то вы можете перехватить его NSubstitute но вы можете сделать это только, если реализация называется N-замещенный. Это работает при обычной отправке методов, потому что при вызове его вызывается наивысший уровень реализации виртуального метода (тот, который предоставляется NSubstitute). Однако это не работает, когда вы вызываете метод с помощью ссылки base.

Таким образом, в то время как вы могли бы перехватить это:

ActivateItem(nextScreen) 

Вы просто не можете перехватить это:

base.ActivateItem(nextScreen); 

Тот факт, что вы используете base.ActivateItem в коде означает, что ваш класс под у теста есть своя реализация метода, который вы не хотите вызывать, поэтому с вашими текущими инструментами вы не можете достичь того, что вы пытаетесь сделать. Вот почему хорошо, что вы нашли workaround.

Вы находитесь в такой же ситуации с большинством других насмешливых фреймворков, включая Moq. Исключением является TypeMock, который использует совершенно другой способ перехвата вызовов методов, что означает, что он может делать то, что другие структуры просто не могут.

+0

спасибо за ваше расследование, я думаю, что это полезное разъяснение для всех приходя в эту нить. Всего 2 бита: я написал base.Xxxx просто для ясности, в коде я просто вызываю 'ActivateItem', и я не переопределяю его (хотя это переопределяемо). Кроме того, перед переключением на NSubstitute я использовал Moq с Moq.AutoMock, и я успешно шпионил с помощью 'mocker.CreateSelfMock ()', а затем 'Mock.Get (viewModel) .Verify (vm => vm.ActivateItem (It .IsAny ())); ' – superjos

+0

@superjos Забавно, это был вызов' base.Xxx', который сделал его интересным. Если это обычный виртуальный вызов, вы можете использовать 'Received' на замене Partial, как вы сказали в своем вопросе, и он должен работать нормально. Замечания по выпуску NSubstitute могут быть немного неправильными, но они, похоже, говорят, что ForPartsOf должен быть в версии 1.7.0 ... https://github.com/nsubstitute/NSubstitute/blob/master/CHANGELOG.txt – forsvarir

+0

Но опять же , проверяемый класс создается AutofacContrib.NSubstitute с automocking, и я не мог видеть ни одного из его методов, которые могли бы вернуть частичную замену. BTW Я играл со своим исходным кодом в VS, и если я не ошибаюсь, я думаю, что ForPartsOf <> недоступен. Так или иначе ... – superjos

1

Таким образом, фактический вопрос не был действительно решил: это просто, что сама проблема исчезла.

Чтобы проверить правильность поведения, я напомнил, что я мог бы также использовать общедоступное свойство ActiveItem из базового класса, поэтому с этим я прекратил использовать Receive() и вернулся к простому сопоставлению значений.

И все же, для справок в будущем, я не нашел способ подсмотреть тест класса под этими библиотеками. Я знаю, что следует избегать шпионажа над тестируемым классом, но, как и во многих вещах, иногда вам нужно это делать.

НТН

Смежные вопросы