Ви б хотіли робити приватні речі внутрішні / загальнодоступні для тестів або використовувати якийсь хак, як PrivateObject?


26

Я досить початківець у тестуванні коду, і раніше був assertблудницею. Одне, що мене хвилює в одиничному тестуванні, - це те, що часто потрібно, щоб ви зробили public(або принаймні internal) поля, які були б privateінакше, щоб скасувати readonlyїх, замість цього зробити privateметоди protected virtualтощо.

Нещодавно я виявив, що ви можете цього уникнути, використовуючи такі речі, як клас PrivateObject, щоб отримати доступ до будь-якого об'єкта за допомогою відображення. Але це робить ваші тести менш рентабельними (речі не вдасться виконати, а не час компіляції; це буде розбито простим перейменуванням, важче налагодити ...). Яка ваша думка з цього приводу? Які найкращі практики тестування одиниць щодо обмеження доступу?

редагувати: врахуйте, наприклад, що у вас є клас із кешем у файлі на диску, а в тестах ви хочете записати на пам'ять.


2
Люди пітону мають лише загальний доступ. Вони щасливі. Чому турбуватися? Чому б не оприлюднити все?
С.Лотт

12
Тому що це далеко не найкраща практика більшості кращих мов. Справжня відповідь - використовувати хороші інтерфейси і таке, щоб ви могли використовувати макетні об’єкти для проведення тестування на.
Ріг

2
@Rig: Python - це не найкраща мова? Цікаво.
С.Лотт

7
"Це далеко не найкраща практика більшості найпопулярніших мов", логічно не передбачає (або навіть натякає на це) "Python не є вищою мовою".
Майк Накіс

1
Саме ця мова є головною мовою, але більшість основних мов не є Python і підтримують відповідну область застосування. Я стою біля своєї заяви. Існують зразки, розроблені для створення програмного забезпечення, яке може бути дуже перевіреним, забезпечуючи збереження змінних обсягів. Навіть програмісти Python, як правило, імітують сферу застосування префіксів із того, що я бачив.
Ріг

Відповіді:


36

Вам ніколи не слід цього робити

зробити public(або принаймні internal) поля, які були б privateінакше, щоб скасувати readonlyїх, замість цього зробити privateметодиprotected virtual

Тим більше, що непрочитане поле може зробити незмінним об'єкт змінним, і це було б катастрофою.

Ваші тести повинні вважати ваші об'єкти чорними полями ( Wikipedia ), тобто вони повинні стосуватися лише публічного інтерфейсу об'єктів, а не деталей їх реалізації.

Якщо об’єкт не може бути достатньо протестований за допомогою його загальнодоступного інтерфейсу, то вам потрібно знайти способи надання формального та корисного розширення його інтерфейсу, який полегшить тестування. Наприклад, система реєстрації, що має лише пару методів Реєстрація / Скасування реєстрації, можливо, отримає користь від методу IsRegistered, навіть якщо це не потрібно для прикладної програми; все-таки це формальне і корисне розширення інтерфейсу, і воно, до речі, вмістить тестування.

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


2
Саме так! Якщо ви не можете перевірити його без роздумів або злому, ви, мабуть, помилилися в своєму API чи дизайні.
Сокіл

Ви хочете сказати, що вважати поганою практикою використання privateметодів, protected virtualщо допомагають знущатися з тестів?
Робула

1
@Robula У моїй книзі так. Я навіть не пишу свої тести в тому ж пакеті, що і виробничий код, тому що я не маю ніякої користі для доступу до непублічних членів. Я також не використовую глузування. Звичайно, якщо вам доведеться використовувати макети, тоді вам доведеться робити все необхідне для полегшення глузування, тому захищений віртуальний може бути практичним компромісом у багатьох ситуаціях. Але в ідеалі дизайн не потребує знущань, лише альтернативні реалізації, а в ідеалі тестування - це тестування в чорному ящику, а не тестування білої коробки, тому речі перевіряються інтеграційно, без знущань.
Майк Накіс

13

У C # ви можете використовувати InternalsVisibleToAttribute щоб дозволити вашій тестовій збірці бачити internalкласи в тестуванні складання Здається, ви це вже знаєте.

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

Це добре піддається ін'єкції залежності та стратегії. Ви можете абстрагувати метод в інтерфейсі, ввести його в конструктор батьківського об'єкта і просто перевірити, чи батьківський делегує належним чином. Потім ви можете перевірити, що дочірній об’єкт поводиться належним чином. Це робить все більш модульним.


8

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

Наскільки легко перевірити клас через його публічний API - це відгуки про те, наскільки легко буде використовувати клас. Подумайте про тести частково як про спосіб прототипу використання вашого класу.

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

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

Також розгляньте можливість використання принципу "сказати не питати", щоб допомогти структурувати інтерфейс класу в надійному, нещільно пов'язаному способі, де потрібна інформація викрита, а решта прихована.


6

Особисто я віддаю перевагу залишити код, який я тестую, настільки чистим, наскільки я можу, і якщо тестовий код потребує злому (і в C ++ неприємних покажчиках тощо), я з радістю це роблю.



1
Я погоджуюсь, це спосіб це зробити. Зберігайте потворність у тестовому коді, де це нічого не може зашкодити.
Алан Делімон

@AlanDelimon Може це не зашкодить розуму наступних програмістів з технічного обслуговування?
flamingpenguin

1
@flamingpenguin некрасивий код може зашкодити програмістам де завгодно; але некрасивий тестовий код, швидше за все, принесе менше шкоди, оскільки вплине лише на людей, що змінюють клас, а також на людей, які його просто споживають.
Ден Нелі

2
@flamingpenguin Це може бути, але якщо вам доведеться десь поставити некрасивий код, він також може бути в тесті одиниць, де менше шансів потребувати модифікації і бути частиною програми.
Алан Делімон

5

Видимість вашого коду не має нічого спільного з вашими одиничними тестами!

Ви робите свої методи приватними не з метою приховувати їх від тестування, і ви не робите їх загальнодоступними для випробувань, правда? Тут важливо це ваша реалізація, а не тести - ті сервери як доказ вашого коду, але вони не ваш код .

Існує маса способів ввімкнути доступ до прихованих (приватних чи внутрішніх) методів та властивостей. Багато тестових рамок і IDE (наприклад, Visual Studio) підтримують вас тут, генеруючи все необхідне для доступу, вам залишається лише автор тесту. Отже, навіщо турбуватися? Краще зберігайте код чистим та акуратним.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.