Вопрос по unit-testing, junit, java, mockito – Когда использовать Mockito.verify ()?

176

Я пишу тестовые примеры jUnit для 3 целей:

Чтобы гарантировать, что мой код удовлетворяет всем требуемым функциям, под всеми (или большинством) входными комбинациями / значениями.Чтобы убедиться, что я могу изменить реализацию, и положиться на контрольные примеры JUnit, чтобы сказать мне, что все мои функциональные возможности все еще удовлетворены.В качестве документации по всем сценариям использования мой код обрабатывается и выступает в качестве спецификации для рефакторинга - если когда-либо потребуется переписать код. (Измените код, и если мои тесты jUnit не пройдут - вы, вероятно, пропустили какой-то вариант использования).

Я не понимаю почему или когдаMockito.verify() должен быть использован. Когда я вижуverify() будучи вызванным, он говорит мне, что мой jUnit узнает о реализации. (Таким образом, изменение моей реализации сломало бы мои jUnits, даже при том, что моя функциональность не была затронута). Я '

ищу:

Какими должны быть рекомендации по правильному использованию?Mockito.verify()

Правильно ли для jUnits быть в курсе или тесно связано с реализацией тестируемого класса?

Я стараюсь держаться подальше от использования verify () столько, сколько могу, по той же причине, по которой вы разоблачили (я неЯ не хочу, чтобы мой модульный тест узнал о реализации), но есть случай, когда у меня нет выбора - заглушенные void-методы. Вообще говоря, поскольку они не возвращают ничего, что они не вносят в ваш вклад »фактический» выход; но все же, вам нужно знать, что это называется. Но я согласен с вами, что нет смысла использовать проверку для проверки потока выполнения. Legna

Ваш Ответ

5   ответов
67

что он вызывает метод B объекта типа C, то вы должны проверить это путем создания макета типа C и проверки того, что метод B был вызван.

Это подразумевает, что контракт класса A имеет достаточную детализацию, чтобы говорить о типе C (который может быть интерфейсом или классом). Так что да, мыречь идет об уровне спецификации, который выходит за рамки просто "Системные Требования"и идет каким-то образом к описанию реализации.

Это нормально для юнит-тестов. Когда вы проводите юнит-тестирование, вы хотите убедиться, что каждый юнит выполняетправильная вещь"и это обычно включает его взаимодействие с другими подразделениями. "Единицы" здесь могут означать классы или большие подмножества вашего приложения.

Обновить:

Я чувствую, что это неЭто относится только к проверке, но также и к заглушке. Как только вы заглушаете метод класса соавтора, ваш модульный тест в некотором смысле становится зависимым от реализации. Это'Такого рода юнит-тесты должны быть такими. Так как Mockito так же важен для проверки, как и для проверки, тот факт, что выиспользование Mockito вообще подразумевает, что выЯ собираюсь столкнуться с такой зависимостью.

По моему опыту, если я изменяю реализацию класса, мне часто приходится менять реализацию его модульных тестов, чтобы соответствовать. Как правило, я выигралт надо изменить инвентарь каких там юнит тестовявляются для класса; если, конечно, причиной изменения было наличие условия, которое я не смог проверить ранее.

Вот что такое юнит-тесты. Тест, который неТакая зависимость от способа использования классов коллабораторов на самом деле является тестом подсистемы или интеграционным тестом. Конечно, они также часто пишутся с помощью JUnit и часто включают использование насмешек. По моему мнению, "JUnit» это ужасное название для продукта, который позволяет нам производить все виды тестов.

@ Рассел Даже если "тип С " интерфейс для оболочки вокруг библиотеки или какой-то отдельной подсистемы вашего приложения? Dawood ibn Kareem
Спасибо, Дэвид. После сканирования некоторых наборов кодов это кажется обычной практикой, но для меня это сводит на нет цель создания модульных тестов и просто добавляет издержки на их обслуживание за очень небольшую ценность. Я понимаю, почему требуются макеты, и почему необходимо настроить зависимости для выполнения теста. Но проверка того, что метод dependencyA.XYZ () выполняется, на мой взгляд, делает тесты очень хрупкими. Russell
Я вас понимаю и полностью с вами согласен. Но я также чувствую, что написание руководящих принципов, которые вы описываете, может привести к написанию всего учебника по TDD или BDD. Чтобы взять ваш пример, позвонивequals() или жеequalsIgnoreCase() никогда не будет тем, что указано в требованиях класса, поэтому никогда не будет модульного теста как такового. Тем не мение, "закрытие соединения с БД по окончании (что бы это ни значило с точки зрения реализации) вполне может быть требованием класса, даже еслине "деловое требование ", Для меня это сводится к отношениям между договором ... Dawood ibn Kareem
@DavidWallace на самом деле код похож на m1 () {…, int a = m2 (); ....} Jack
51

David»ответ, конечно, правильный, но неЯ не могу объяснить, почему вы этого хотите.

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

Например, у вас есть немного бизнес-логики, которая должна хранить вещи с использованием DAO. Вы можете сделать это с помощью интеграционного теста, который создает экземпляр DAO, подключает его к бизнес-логике, а затем копается в базе данных, чтобы увидеть, сохранен ли ожидаемый материал. Тот'больше не является модульным тестом.

Или вы могли бы посмеяться над DAO и убедиться, что он вызывается так, как вы ожидаете. С помощью mockito вы можете убедиться, что что-то вызывается, как часто оно вызывается, и даже использовать сопоставления параметров, чтобы убедиться, что оно вызывается определенным образом.

Обратная сторона модульного тестирования, подобного этому, заключается в том, что вы привязываете тесты к реализации, что делает рефакторинг немного сложнее. С другой стороны, хороший дизайнерский запах - это количество кода, необходимого для его правильного использования. Если ваши тесты должны быть очень длинными, возможно, что-то не так с дизайном. Так что код с большим количеством побочных эффектов / сложных взаимодействий, которые необходимо протестировать, вероятно, не очень хорошая вещь.

0

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

Что касается вашего беспокойства по поводу нарушения ваших тестов при рефакторинге, то это вполне ожидаемо при использовании mocks / stubs / spies. Я имею в виду, что по определению, а не в отношении конкретной реализации, такой как Mockito. Но вы могли бы подумать так: если вам нужно провести рефакторинг, который приведет к серьезным изменениям в том, как работает ваш метод, это хорошая идея сделать это на подходе TDD, то есть вы можете изменить свой тестпервый определить новое поведение (которое провалит тест), изатем сделайте изменения и получите тест пройден снова.

8

что вы абсолютно правы от классического подхода »Точка зрения:

Если вы первыйсоздать (или изменить) бизнес-логику вашего приложения, а затемпокрыть это (принять) тесты (Тест-Последний подход), тогда будет очень больно и опасно сообщать тестам что-либо о том, как работает ваше программное обеспечение, кроме проверки входов и выходов.Если вы практикуетеТест-управляемый подходтогда ваши тестыбыть первым написанным, подлежащим изменению и отражающим варианты использования вашего программного обеспеченияс функциональностью.Реализация зависит от тестов. Это иногда означает, что вы хотите, чтобы ваше программное обеспечение было реализовано определенным образом, например, полагаться на какой-то другой компонент "S метод или даже назвать его определенное количество раз. Вот гдеMockito.verify () пригодится!

Важно помнить, что нет универсальных инструментов. Тип программного обеспечения, это 'Размер, цели компании и рыночная ситуация, командные навыки и многое другое влияют на решение о том, какой подход использовать в вашем конкретном случае.

24

что основной причиной этого является то, что мы используем JUnit не только для модульного тестирования. Таким образом, вопрос должен быть разделен:

Должен ли я использовать Mockito.verify () в моеминтеграция (или любое другое тестирование выше единицы)?Должен ли я использовать Mockito.verify () в моемчерный ящик Блок-тестирование?Должен ли я использовать Mockito.verify () в моембелая коробка Блок-тестирование?

так что если мы будем игнорировать тестирование выше единицы, вопрос можно перефразировать »С помощьюбелая коробка Юнит-тестирование с помощью Mockito.verify () создает отличную пару между юнит-тестированием и моей реализацией.серый ящик» юнит-тестирование и какие практические правила я должен использовать для этого ».

Теперь давайтепройти через все это шаг за шагом.

* - Должен ли я использовать Mockito.verify () в моеминтеграция (или любое другое тестирование выше единицы)? Я думаю, что ответ однозначно нет, более того, вы не должныНе используйте насмешки для этого. Ваш тест должен быть максимально приближен к реальному применению. Вы тестируете полный вариант использования, а не изолированную часть приложения. *

черный ящик противбелая коробка Блок-тестирование* Если вы используетечерный ящик подходить к тому, что вы действительно делаете, вы предоставляете (все классы эквивалентности) вход,государствои тесты, которые вы получите ожидаемый результат. При таком подходе использование макетов в целом оправдывает себя (вы просто имитируете, что они поступают правильно; вы нея не хочу их проверять), но вызывать Mockito.verify () излишне.

Если вы используетебелая коробка подходить к тому, что ты действительно делаешь, тыповторное тестированиеповедение вашего подразделения. В этом подходе необходим вызов Mockito.verify (), вы должны убедиться, что ваш модуль ведет себя так, как выожидаю.

правила превью для серого бокса Проблема тестирования белого ящика заключается в том, что он создает высокую связь. Одним из возможных решений является тестирование «серого ящика», а не «белого ящика». Это своего рода комбинация черного итестирование белого ящика. Вы действительно тестируетеповедение вашего модуля, как в тесте белого ящика, но в целом вы делаете его независимым от реализациикогда возможно, Когда это возможно, вы просто делаете проверку, как в случае черного ящика, просто утверждаете, что выходной результат - это то, что вы ожидаете. Итак, суть вашего вопроса в том, когда это возможно.

Это действительно сложно. Я нехороший пример, но я могу привести примеры. В случае, который был упомянут выше с помощью equals () и equalsIgnoreCase (), вы не должныt вызвать Mockito.verify (), просто подтвердить вывод. Если бы ты не могне делайте этого, разбивайте свой код на меньшие единицы, пока не сможете это сделать. С другой стороны, предположим, что у вас есть некоторый @Service и вы пишете @ Web-Service, который по сути является оберткой для вашего @Service - он делегирует все вызовы @Service (и делает некоторую дополнительную обработку ошибок). В этом случае вызов Mockito.verify () необходим, вы не должныt дублировать все ваши проверки, которые вы сделали для @Serive, подтверждая, что вы 'повторного вызова @Service с правильным списком параметров достаточно.

Тестирование серая коробка - немного ловушка. Я склонен ограничивать это такими вещами, как DAO. Я'Я участвовал в некоторых проектах с очень медленной сборкой из-за обилия тестов «серого ящика», почти полного отсутствия модульных тестов и слишком большого количества тестов «черного ящика», чтобы компенсировать недостаток доверия к тому, что якобы тестировали тесты greybox. Jilles van Gurp

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