Чому Mockito не знущається над статичними методами?


267

Я прочитав тут кілька тем про статичні методи, і думаю, що розумію проблеми, які можуть спричинити неправильне використання / надмірне використання статичних методів. Але я дійсно не дійшов до низу, чому важко знущатися над статичними методами.

Я знаю, що інші глузуючі рамки, такі як PowerMock, можуть це зробити, але чому Mockito не може?

Я читав цю статтю , але автор, здається, релігійно проти цього слова static, можливо, це моє погане розуміння.

Просте пояснення / посилання було б чудово.


12
Лише побічна примітка: PowerMock не є модельною бібліотекою об'єктів, вона просто додає ті функції (глузування зі статики та ctors) поверх інших бібліотек. Ми використовуємо PowerMock + Mockito в роботі, вони добре плавають один з одним.
Маттіас

Відповіді:


238

Я думаю, що причина може полягати в тому, що макетні бібліотеки об'єктів зазвичай створюють макети, динамічно створюючи класи під час виконання (за допомогою cglib ). Це означає, що вони або реалізують інтерфейс під час виконання (саме це робить EasyMock, якщо я не помиляюся), або вони успадковують з класу глузування (саме це робить Mockito, якщо я не помиляюся). Обидва підходи не працюють для статичних членів, оскільки ви не можете їх перекрити, використовуючи успадкування.

Єдиний спосіб знущатися над статикою - це змінити байт-код класу під час виконання, який, я думаю, є дещо більше, ніж спадкування.

Це я здогадуюсь, для чого це варто ...


7
Те саме стосується і глузуючих конструкторів, до речі. Вони теж не можуть бути змінені через спадщину.
Маттіас

11
Можливо, варто також додати, що деякі прихильники TDD / TBD сприймають відсутність статичних методів та знущань із конструкторів як добру річ. Вони стверджують, що коли вам доводиться знущатися над статичними методами чи конструкторами, то це показник поганого дизайну класів. Наприклад, дотримуючись пуристичного підходу IoC при складанні модулів коду, вам ніколи навіть не виникатиме необхідності знущатися над статикою або цитатами в першу чергу (якщо, звичайно, вони не є частиною компонента чорного поля). Дивіться також giorgiosironi.blogspot.com/2009/11/…
Маттіас

200
Я думаю, що глузуючі інструменти повинні дати вам те, що вам потрібно, не припускаючи, що вони знають, що краще для вас. Наприклад, якщо я використовував сторонню бібліотеку, яка використовувала статичний виклик методу, який мені потрібно знущатися, було б непогано мати можливість це зробити. Ідея про те, що макет-фреймворк не надасть вам певних можливостей, оскільки його сприймають як поганий дизайн, принципово хибний.
Ло-Тан

11
@ Lo-Tan - це як би сказати, що мова повинна бути здатна на все, не вважаючи, що вона знає краще, ніж ти. Це просто марнота з вашого боку, тому що вони здаються нав'язливими. Проблема тут полягає в тому, що "анти / простатичний" бій незрозумілий, а також такі рамки. Я згоден, що у нас повинно бути і те, і інше. Але де факти є ясно, я вважаю за краще рамки , які нав'язують ці факти. Це один із способів навчання - інструменти, які підтримують вас на шляху. Тож вам самим цього не потрібно. Але тепер кожна голова локшини може нав'язати свій так званий «гарний дизайн». "Принципово хибний" ...
nevvermind

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

28

Якщо вам потрібно знущатися над статичним методом, це сильний показник для поганого дизайну. Зазвичай ви знущаєтесь над залежністю свого тестувального класу. Якщо ваш тест-клас під тестом посилається на статичний метод - наприклад, java.util.Math # sin, наприклад, це означає, що тестуванню класу потрібна саме ця реалізація (наприклад, точність та швидкість). Якщо ви хочете абстрагуватися від конкретної синусової реалізації, вам, мабуть, потрібен інтерфейс (ви бачите, куди це йде)?


3
Що ж, я використовував статичні методи для надання абстракцій високого рівня, таких як "фасад статичної стійкості". Такий фасад захищає код клієнта від складності та деталей низького рівня API ORM, забезпечуючи більш послідовний та простий у користуванні API, дозволяючи при цьому багато гнучкості.
Rogério

І навіщо вам це знущатися? Якщо ви залежите від статичного методу, ви "одиниця" або "модуль" - це не один клас, але також включає "фасад статичної стійкості".
Jan

88
Щоправда, але іноді у вас може не бути вибору, якщо, наприклад, вам потрібно знущатися над статичним методом, який знаходиться в якомусь третьому класі.
Stijn Geukens

6
Щоправда, але іноді ми можемо мати справу з одинаками.
Ману Манджунат

Єдина думка, яку неможливо вирішити шляхом абстрагування, - це занадто багато рівнів абстракції ... Додавання шарів абстракції додає складності і часто є зайвою. Я думаю про приклади я бачив рамки, які намагаються знущатися з System.currentTimeMillis (), перетворюючи цей простий дзвінок в один клас. Ми закінчуємо одиночним класом за методом замість того, щоб просто мати методи - лише для полегшення тестування. І тоді, коли ви запроваджуєте сторонній деп, який викликає статичний метод безпосередньо замість вашої одиночної обгортки, тести все-таки пройдуть ...
Отець Джеремі Кріг,

5

Я серйозно думаю, що це кодовий запах, якщо вам також потрібно знущатися над статичними методами.

  • Статичні методи доступу до загальної функціональності? -> Використовуйте одиночний екземпляр і введіть це
  • Код сторони? -> Загорніть його у свій власний інтерфейс / делегат (і при необхідності зробіть його також синглтоном)

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

PS: Те ж саме стосується і приватних методів. Я не думаю, що тести повинні знати про деталі приватних методів. Якщо клас настільки складний, що він спокушається знущатися над приватними методами, це, мабуть, знак розділити цей клас ...


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

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

@ pete83 Я чую, брат. Однак у мене є проблема з тестовими вкладками або рамками, які вимагають від розробників змінити свій дизайн, щоб відповідати дизайну / обмеженням тестових рамок. Тобто ІМО ставить візок перед конем, або хвіст, що висить собаку.
Метт Кемпбелл

1
Цей аргумент для мене мало сенсу. Однотонні зразки вже не зважають на довгі роки, тому тут перераховується занадто багато причин. Що являє собою "чистий" код? Якщо у мене є метод екземпляра класу, який викликає статичний хелперний метод, який повертає деяку операцію вводу / виводу, чому я не хочу, щоб це висміяли в тесті? І як це поганий дизайн? Все це віджимання рук навколишніми глузуючими статичними методами не складається. Знущання над методом - протилежне тестуванню. Якщо це занадто важко втілити, то просто скажіть це і
зробіть

О, чоловіче, я ніколи не говорив про те, що було в школі для одиночної школи Singleton, куди всі дзвонять Foo.getInstance()скрізь. Я щойно писав синглтон у відповідь, щоб протиставити аргумент, "але статичний метод не вимагає створення багатьох об'єктів обгортки". Крім того, для мене концептуально мало різниці між статичним методом та методом екземпляра на синглетоні, лише те, що ви не можете знущатися над цим однотонним співавтором. Але одиночне чи ні - це зовсім не справа, яку я намагався зробити, справа полягає в тому, щоб вводити і знущатися над співробітниками, а не викликати статичні методи, якщо це робить тестування важким.
pete83

4

Mockito повертає об'єкти, але статичний означає "рівень класу, а не рівень об'єкта". Отже, mockito дасть нульовий вказівник виключення для статичного.


0

У деяких випадках статичні методи можуть бути важкими для перевірки, особливо якщо з них потрібно знущатися, саме тому більшість глузуючих фреймворків не підтримують їх. Мені здалося, що цей запис у блозі є дуже корисним у визначенні способів знущатися над статичними методами та класами.


1
Знущання над статичними методами навіть простіше, ніж знущання над методами екземплярів (оскільки немає примірника) при використанні відповідного знущаючого API.
Rogério

Це як відповісти на запитання самим питанням, через що це важко зробити, на що це не відповідь.
Маттіас

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