Чи повинні приватні / захищені методи проходити модульний тест?


82

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

Тепер моє запитання стосується приватних та захищених методів, які мені, можливо, доведеться писати у своєму класі на підтримку методів / властивостей, виставлених інтерфейсом:

  • Чи повинні приватні методи в класі мати власні модульні тести?

  • Чи повинні захищені методи в класі мати власні модульні тести?

Мої думки:

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

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

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


1
Якщо ви не застосовуєте свої захищені методи від своїх тестів, ні замінюючи їх, ні викликаючи їх, чому вони захищені, а не приватні? Роблячи їх захищеними, ви приймаєте свідоме рішення виставити точку розширення / функціональність. Для мене, якщо ви дотримуєтеся TDD, це рішення повинно визначатися тестами, які ви пишете.
forsvarir

2
Ви повинні помістити частину про власні думки в окрему відповідь. Повідомте мене, коли ви це зробите, і я підтримую.
Кіт Пінсон,


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

Відповіді:


108

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

Отже, ці приватні методи перевіряються неявно тестами, які стверджують поведінку вашого загальнодоступного інтерфейсу.

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


23
Мені подобається те, що ви сказали, що модульні тести перевіряють поведінку, а не методи! Це набагато з’ясовує речі.
Радж Рао,

1
Я згоден з @rajah. Це має бути першим твердженням кожного підручника. Я роздумував, як перевірити свої методи, тепер я знаю, що не потрібно. +1
морозний дивовижний

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

1
Взагалі кажучи, шаблони, що сприяють розділенню проблем, більше підходять для ізольованих модульних тестів, тоді як шаблони, що сприяють інкапсуляції, простіше використовувати API.
尤川豪

3
Це не дає зрозуміти випадку захищеної видимості. Здається, захищений метод також є частиною інтерфейсу, часто це точка розширення, спеціально зроблена захищеною, щоб бути такою. Я б сказав, що в таких випадках вам слід також їх юніт-тестувати. Ви не хочете, щоб хтось змінював речі в майбутньому і ламав класи, які залежали від цих точок розширення поведінки.
Дідьє А.

45

Я не згоден з більшістю плакатів.

Найважливішим правилом є: РОБОЧИЙ КОДЕКС ТЕРМІЧНІ ПРАВИЛА про публічне / захищене / приватне.

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

Якщо ви не можете, то або рефакторіть, щоб ви могли, або зігніть захищені / приватні правила.

Існує чудова історія про психолога, який дав дітям тест. Він дав кожній дитині дві дерев'яні дошки з мотузкою, прикріпленою до кожного кінця, і попросив їх якомога швидше перетнути кімнату, не торкаючись ніг до підлоги. Усі діти використовували дошки, як маленькі лижі, по одній нозі на кожну дошку, тримаючи їх за мотузки і ковзаючи по підлозі. Потім він дав їм те саме завдання, але використовуючи лише ОДНУ дошку. Вони оберталися / «ходили» по підлозі, однією ногою на кожному кінці єдиної дошки - і вони були ШВИДШЕ!

Те, що Java (або будь-яка інша мова) має функцію (приватну / захищену / загальнодоступну), не обов’язково означає, що ви пишете кращий код, оскільки ви ним користуєтесь!

Зараз завжди будуть способи оптимізувати / мінімізувати цей конфлікт. У більшості мов ви можете зробити метод захищеним (замість загальнодоступного) і помістити тестовий клас в той самий пакет (або будь-який інший), і метод буде доступний для тестування. Є анотації, які можуть допомогти, як описано в інших плакатах. Ви можете використовувати роздуми, щоб отримати приватні методи (юк).

Контекст також має значення. Якщо ви пишете API для використання зовнішніми людьми, важливим є публічний / приватний. Якщо це внутрішній проект - кого насправді це цікавить?

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


3
Якщо метод критичний і має складну логіку, твердження про свою поведінку дуже корисно для запобігання помилкам. Написання юніт-тесту для такого методу може навіть допомогти, оскільки ви впроваджуєте метод свого роду дослідницьким способом. Тож навіть якщо це приватно, я б сказав, що це варто для модульного тестування. АЛЕ, і є велике, але, ви повинні пам’ятати, тести - це зв’язок коду. Якщо ви пишете тест для методу, ви запобігаєте рефакторингу.
Дідьє А.

6
Тому, перш ніж займатись написанням тестів для приватних методів, я б сказав, що завжди переосмислюйте свій дизайн. Подивіться, чи можна речі узагальнити та перетворити на чисто функціональні методи. Якщо так, ви можете виділити їх у власну конструкцію. Потім ця конструкція може мати власний загальнодоступний інтерфейс і бути модульно протестованою. Пам’ятайте, часто випадки ускладненої поведінки в приватних методах можуть бути ознакою того, що клас несе більше ніж одну відповідальність. Тож спочатку переосмисліть свій дизайн.
Didier A.

Так, але що таке "робочий код"? Тестування приватного методу нічого не говорить про те, чи має ваш об'єкт правильну поведінку. Це головне, чому ми перевіряємо лише публічні методи. Лише загальнодоступні методи демонструють поведінку, про яку дбає користувач фрагмента коду.
Саммі

1
"Робочий код" - це код, який працює. Якщо у вашому приватному (або квазіприватному) методі є помилка, яка не виявляється тестами для ваших загальнодоступних методів, то щось не так. Можливо, ваш дизайн неправильний, досить справедливий: я згоден, що найкращим рішенням є тести, які називають загальнодоступними методами. Але це не завжди можливо, особливо якщо ви додаєте або виправляєте застарілий код. (Я говорю з досвіду щодо проекту з 1 мільйоном рядків коду.) Перевірений код завжди кращий за неперевірений код, крапка. Навіть якщо ми порушили хороші правила про тестування лише загальнодоступних методів!
Чарльз Рот,

Біт (у верхній частині) про тести "тести - це зчеплення коду ... запобігання рефакторингу" є 100% помилковим. В архітектурній метафорі випробування є будівельними лісами, а не конкретними. Річ змінюється, тести змінюються, викидаються, пишуться нові тести. Я згоден з тим, що хороший дизайн мінімізує переписування тестів. Але зміни трапляються навіть у найкращих конструкціях.
Чарльз Рот,

34

Ви написали:

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

Будь ласка, дозвольте мені переформулювати це мовою BDD :

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

* Може бути фактичним Interfaceабо просто доступним API класу, наприклад: Ruby не має інтерфейсів.

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

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

Сподіваюся, це допомагає!


1
Блискучий пост. Багато уточнює.
морозний дивовижний

17

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

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


13

Немає! Тільки тестові інтерфейси.

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


11

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

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


9

Існує дві причини для написання тестів:

  1. Ствердження очікуваної поведінки
  2. Запобігання регресу поведінки

Прийняття (1) Відстоювання очікуваної поведінки:

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

  • Чи спрацювало те, що я щойно писав?
  • Цей цикл насправді закінчується?
  • Це цикл у тому порядку, який, на мою думку, є?
  • Чи буде це працювати для нульового вводу?

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

Це гарна ідея робити це там, де ви вважаєте за потрібне. Будь-який код, який трохи хитро зрозуміти або є нетривіальним. Навіть тривіальний код може отримати від цього користь. Вся справа у вашій власній впевненості. Як часто це робити і як далеко їхати, залежатиме від вашого власного задоволення. Зупиніться, коли зможете впевнено відповісти «Так»: Ви впевнені, що це працює?

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

Прийняття (2) Запобігання регресу поведінки:

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

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

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

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

Це ваш типовий модульний тест, про який більшість говорять, згадуючи TDD або BDD. Справа в тому, щоб загартувати межі та захистити їх від змін. Ви не хочете перевіряти для цього приватні методи, оскільки приватний метод не є межею. Захищені методи - це обмежені межі, і я б їх захистив. Вони не потрапляють у світ, але все одно потрапляють в інші відсіки або "одиниці".

Що з цього робити?

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

Так і ні.

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

Нарешті : напишіть тести для своїх меж. Проведіть одиничний тест для кожної точки, який буде використовуватися іншими одиницями вашої системи. Переконайтеся, що цей тест підтверджує семантичний контракт, назву методу, кількість аргументів тощо. А також переконайтеся, що тест підтверджує доступну поведінку блоку. Ваш тест повинен продемонструвати, як користуватися приладом, і що прилад може робити. Зберігайте ці тести увімкненими, щоб вони виконувались при кожному натисканні коду.

ПРИМІТКА. Причиною того, що ви вимкнули перший набір тестів, є можливість роботи з рефакторингу. Активний тест - це зв’язок коду. Це запобігає подальшим змінам коду, який він тестує. Це потрібно лише для ваших інтерфейсів та договорів про взаємодію.


1
Здається, якщо ви явно не тестуєте приватні методи ізольовано, вони не охоплені вашими тестами, і ви не можете довіряти їм, що вони працюють. Я стверджую, що це просто неправильно. Приватний метод (або будь-який шлях до нього в коді), який не можна перевірити за допомогою загальнодоступного методу, є мертвим кодом і його слід видалити. Вся суть TDD полягає в тому, щоб отримати повне охоплення лише тестуванням загальнодоступних методів, тому що ви пишете 0 LoC, які не існують, щоб пройти тест. Тестування ізольованого приватного методу служить ТІЛЬКИ для того, щоб зробити рефакторинг складнішим, прямо протилежним (одній із) цілей TDD.
сара

@kai Я прямо заявляю, що ви не повинні мати автоматизованих тестів для приватних методів, але іноді корисно мати ізольовані тести, які допоможуть вам із впровадженням. Ці тести не повинні бути частиною вашого набору тестів, або їх слід відключити з тієї самої причини, яку ви згадали: рефакторинг. Вирішувати, коли ви віддаєте перевагу програмуванню для впровадження приватного методу чи ні, залежить від вашого власного рівня впевненості. Може, ви не читали до кінця моєї відповіді?
Дідьє А.

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

@kai Ви згадуєте: "або він мертвий та / або неперевірений код". Неперевірений код - це те, про що я говорю. Приватний метод може приховати від загальнодоступних методів багато помилок, до яких не застосовуються крайні випадки. Уявіть собі вимкнення на одну помилку. Іноді інваріанти публічних методів роблять це так, що ця справа ніколи не відбудеться. У такому випадку я вважав би, що приватний метод все ще глючить і має хибну реалізацію, проте його інтеграція не дозволяє виявити та вловити помилку. У цьому випадку вам може знадобитися кілька тестів для випробування крайових випадків, щоб ви могли бути впевнені, що ваш метод не містить помилок.
Дідьє А.

@kai Але, зрозумійте, що ті тести, про які я говорю, не є вашим набором тестів, це не TDD. Я кажу, що деякі приватні методи полегшуються для реалізації, якщо ви можете швидко запустити деякі тести проти них. У мовах з REPL це вам не потрібно стільки. І ви можете спробувати пройти через метод у своїй голові, але я рекомендую замість цього проводити комп’ютерні тести лише для складних для реалізації приватних методів. Я пропоную після цього видалити тести або залишити їх вимкненими або у своєму спеціальному місці, яке не запускається у вашій збірці CI.
Дідьє А.

4

Ні, не слід випробовувати приватні методи (як би ви все-таки зробили, не використовуючи чогось жахливого, як рефлексія). З захищеними методами це трохи менш очевидно в C #, ви можете зробити речі захищеними внутрішніми, і я думаю, це добре робити, щоб перевірити похідні класи, які реалізують всю їх функціональність за допомогою методів шаблонових шаблонів.

Але, загалом, якщо ви думаєте, що ваші загальнодоступні методи роблять занадто багато, настав час перетворити ваші класи на більш атомні класи, а потім перевірити ці речення.


2

Я теж погоджуюсь з відповіддю @ kwbeam про тестування приватних методів. Однак важливий момент, який я хотів би підкреслити - захищені методи - це частина експортованого API класу, а отже ПОВИННА тестуватися.

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

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

Сподіваюся, це допоможе!


2

Якщо ви прагнете до високого охоплення коду (я пропоную вам це зробити), вам слід протестувати всі свої методи, незалежно від того, є вони приватними чи захищеними.

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

Просто приховування методу для клієнта (створення приватного) не дає йому права мати право не проходити аудит. Тому їх можна перевірити загальнодоступними методами, як уже згадувалося раніше.


1

Я згоден з усіма іншими: Відповідь на ваше запитання - "ні".

Справді, ви цілком праві з підходом та думками, особливо щодо висвітлення коду.

Я також додам, що питання (і відповідь "ні") стосується також загальнодоступних методів, які ви можете ввести до класів.

  • Якщо ви додаєте методи (загальнодоступні / захищені чи приватні), оскільки вони роблять невдалий тест, то ви більш-менш досягли мети TDD.
  • Якщо ви додаєте методи (загальнодоступні / захищені чи приватні), оскільки ви просто вирішили, порушуючи TDD, тоді покриття коду повинно їх охопити, і ви зможете вдосконалити свій процес.

Крім того, для С ++ (і я повинен думати лише для С ++) я реалізую інтерфейси, використовуючи лише приватні методи, щоб вказати, що клас повинен використовуватися лише через інтерфейс, який він реалізує. Це зупиняє мене помилково викликати нові методи, додані до моєї реалізації з моїх тестів


0

Хороший дизайн означає розділення програми на кілька перевірених одиниць. Після цього деякі блоки піддаються загальнодоступному API, а деякі інші можуть і не бути. Крім того, точки взаємодії між опроміненими одиницями та цими "внутрішніми" одиницями також не є частиною лобкового API.

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

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