Почему Mockito не высмеивает статические методы?


267

Я прочитал несколько веток здесь о статических методах, и я думаю, что я понимаю проблемы неправильного использования / чрезмерного использования статических методов. Но я не до конца понял, почему трудно высмеивать статические методы.

Я знаю, что другие насмешливые фреймворки, такие как PowerMock, могут это делать, но почему не может Mockito?

Я читал эту статью , но автор, похоже, религиозно против слова static, возможно, это мое плохое понимание.

Простое объяснение / ссылка было бы здорово.


12
Просто примечание: PowerMock сам по себе не является библиотекой фиктивных объектов, он просто добавляет эти функции (статическая статика и ctors) поверх других библиотек. Мы используем PowerMock + Mockito на работе, они хорошо плавают друг с другом.
Матиас

Ответы:


238

Я думаю, что причина может заключаться в том, что библиотеки фиктивных объектов обычно создают фиктивные путем динамического создания классов во время выполнения (используя cglib ). Это означает, что они либо реализуют интерфейс во время выполнения (это то, что делает EasyMock, если я не ошибаюсь), либо они наследуют от класса для имитации (это то, что делает Mockito, если я не ошибаюсь). Оба подхода не работают для статических членов, поскольку вы не можете переопределить их, используя наследование.

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

Это мое предположение, для чего это стоит ...


7
Кстати, то же самое верно и для насмешливых конструкторов. Их тоже нельзя изменить с помощью наследования.
Матиас

11
Также стоит добавить, что некоторые сторонники TDD / TBD считают отсутствие статического метода и насмешки над конструкторами хорошей вещью. Они утверждают, что когда вам приходится издеваться над статическими методами или конструкторами, это показатель плохого проектирования классов. Например, следуя пуристическому подходу IoC при сборке модулей кода, у вас никогда даже не возникнет необходимости макетировать статику или ctors в первую очередь (если они, конечно, не являются частью какого-либо компонента черного ящика). Смотрите также giorgiosironi.blogspot.com/2009/11/…
Матиас

201
Я думаю, что инструменты для насмешки должны дать вам то, что вам нужно, не предполагая, что они знают, что для вас лучше. Например, если бы я использовал стороннюю библиотеку, которая использовала статический вызов метода, который мне нужно было смоделировать, было бы неплохо сделать это. Идея о том, что фальшивый фреймворк не даст вам никаких возможностей, потому что он рассматривается как плохой дизайн, в корне ошибочна.
Ло-Тан

11
@ Ло-Тан - это все равно что говорить, что язык должен быть способен на все, не предполагая, что он знает лучше тебя. Это просто суета с вашей стороны, потому что они кажутся внушительными. Проблема здесь в том, что «анти / про-статическая» битва не ясна, как и основы. Я согласен, что мы должны иметь оба. Но где факты являются ясно, я предпочитаю рамки , которые навязывают эти факты. Это один из способов обучения - инструменты, которые помогут вам в правильном направлении. Так что ты сам не обязан. Но теперь каждая голова лапши может навязать свой так называемый «хороший дизайн». "Принципиально ущербный" ...
nevvermind

13
@nevvermind А? Язык высокого уровня призван помочь вам и иметь необходимые абстракции, чтобы вы могли сосредоточиться на важных частях. Библиотека тестирования - это инструмент - инструмент, который я использую для создания более качественного и, надеюсь, лучшего кода. Какой смысл в библиотеке тестирования / макета, когда у нее есть ограничения, которые могут означать, что я не могу использовать ее, когда мне приходится интегрировать чужой плохо разработанный код? Не кажется хорошо продуманным, в то время как хорошие языки были .
Ло-Тан

28

Если вам нужно издеваться над статическим методом, это сильный показатель плохого дизайна. Обычно вы издеваетесь над зависимостью вашего тестируемого класса. Если ваш тестируемый класс ссылается на статический метод - например, java.util.Math # sin - это означает, что тестируемому классу нужна именно эта реализация (например, точность или скорость). Если вы хотите абстрагироваться от конкретной реализации пазухи, вам, вероятно, нужен интерфейс (вы видите, к чему это приведет)?


3
Ну, я использовал статические методы для обеспечения абстракций высокого уровня, таких как «фасад статической персистентности». Такой фасад защищает клиентский код от сложностей и низкоуровневых деталей API-интерфейса ORM, обеспечивая более согласованный и простой в использовании API-интерфейс, одновременно обеспечивая большую гибкость.
Рожерио

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

89
Верно, но иногда у вас может не быть выбора, если, например, вам нужно смоделировать статический метод, который находится в каком-то стороннем классе.
Stijn Geukens

7
Правда, но иногда мы можем иметь дело с одиночками.
Ману Манджунат

Единственная мысль, которая не может быть решена путем абстрагирования, - это слишком много уровней абстракции ... Добавление слоев абстракции добавляет сложности и часто не требуется. Я думаю о примерах, которые я видел для фреймворков, которые пытаются смоделировать System.currentTimeMillis (), заключая этот простой вызов в один класс. В итоге мы получаем синглтон-класс для каждого метода вместо того, чтобы просто иметь методы - только для облегчения тестирования. А потом, когда вы вводите сторонний dep, который вызывает статический метод напрямую, а не через одноэлементную оболочку, тесты все равно не пройдут ...
Отец Джереми Криг

5

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

  • Статические методы для доступа к общей функциональности? -> Используйте экземпляр Singleton и введите
  • Сторонний код? -> Оберните его в свой собственный интерфейс / делегат (и при необходимости сделайте его тоже одиночным)

Единственный раз, когда это кажется мне излишним, это библиотеки вроде Guava, но вам не нужно все равно издеваться над этим, потому что это часть логики ... (например, Iterables.transform (..))
Таким образом, ваш собственный код остается чистым, вы можете смоделировать все ваши зависимости в чистом виде, и у вас есть антикоррупционный слой против внешних зависимостей. Я видел PowerMock на практике, и все классы, для которых он был нужен, были плохо спроектированы. Также временами интеграция PowerMock вызывала серьезные проблемы
(например, https://code.google.com/p/powermock/issues/detail?id=355 ).

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


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

Я не говорил, что всем рекомендую Singleton Pattern. Я имел в виду, что если бы мне пришлось выбирать между статическим служебным классом и Singleton, предлагающим ту же функциональность, я бы выбрал Singleton. И если класс является Singleton или нет, все равно должен управляться инфраструктурой DI, в моем классе I @Inject SomeDependencyи в моей конфигурации я определяю bind(SomeDependency.class).in(Singleton.class). Таким образом, если завтра это больше не Singleton, я изменю один конфиг и все.
pete83

@ pete83 Я слышу тебя, брат. Однако у меня есть проблема с тестовыми библиотеками или фреймворками, требующими, чтобы разработчики изменили свой дизайн, чтобы соответствовать дизайну / ограничениям фреймворка. То есть ИМО ставит телегу перед лошадью или хвостом, виляющим собакой.
Мэтт Кэмпбелл

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

О, чувак, я никогда не говорил о той старой школьной модели Синглтона, где все звонят Foo.getInstance()везде. Я просто написал синглтон в ответ, чтобы противостоять аргументу, «но статический метод не требует создания множества объектов-оболочек». Кроме того, концептуально для меня есть небольшая разница между статическим методом и методом экземпляра в синглтоне, просто вы не можете издеваться над этим синглтоном. Но синглтон или нет - это совсем не то, что я пытался сделать, смысл в том, чтобы вводить и высмеивать коллабораторов, а не вызывать статические методы, если это затрудняет тестирование.
pete83

4

Mockito возвращает объекты, но static означает «уровень класса, а не уровень объекта», поэтому mockito выдаст исключение нулевого указателя для static.


0

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


1
Пересмешивать статические методы даже проще, чем разыгрывать методы экземпляров (так как экземпляра нет) при использовании подходящего API пересмешивания.
Рожерио

Это все равно, что отвечать на вопрос самим вопросом, поэтому трудно было это сделать, на что это не ответ.
Матиас

40
Я отказался от него, потому что в блоге рекомендуется дорогостоящий обходной путь (рефакторинг рабочего кода), а не решение проблемы изоляции класса от статических методов, которые он использует. ИМО, насмешливый инструмент, который действительно выполняет свою работу, не будет дискриминировать методы любого рода; разработчик должен иметь возможность самостоятельно решать, является ли использование статических методов хорошим или плохим в данной ситуации, а не вынужден идти по одному пути.
Rogério
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.