Чому одиничні методи перевірки приватних методів вважаються поганою практикою?


18

Контекст:

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

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

Проблема:

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

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

Питання:

Мені важливо дотримуватися кращих практик і хотів би зрозуміти:

  • Чому використання одиничних тестів приватними / прихованими методами погано (який ризик)?
  • які найкращі практики, коли публічні методи можуть використовувати низький рівень та / або складну обробку?

Рекомендації:

  • це не спосіб сумніву. У Python немає правдивої концепції конфіденційності, а приховані методи просто не перелічені, але їх можна використовувати, коли ви знаєте їх ім'я
  • Мене ніколи не вчили правилам та моделям програмування: останні мої заняття з 80-х… Я в основному вивчав мови шляхом проб і помилок та посилань в Інтернеті (Stack Exchange є моїм улюбленим протягом багатьох років)


2
ОП, де ви чули чи читали, що тестування приватних методів вважалося "поганим"? Існують різні способи одиничного тестування. Див. Тестування одиниць, тестування в чорному ящику та тестування в білій коробці .
Джон Ву

@JohnWu: Я знаю різницю між тестуванням White-box і Black-box тестуванням. Але навіть у тестуванні White-box схоже, що необхідність тестування приватних методів є натяком на проблему дизайну. Моє запитання - це спроба зрозуміти, які найкращі шляхи, коли я потрапляю туди ...
Серж Баллеста

2
Знову ж таки, де ви чули чи читали, що навіть у тестуванні White-box необхідність тестування приватних методів є натяком на проблему дизайну? Я хотів би зрозуміти міркування, які стоять за цією вірою, перш ніж намагатися відповісти.
Джон Ву

@SergeBallesta, іншими словами, покладіть деякі посилання на ті статті, які змусили вас вважати, що тестування приватних методів є поганою практикою. Тоді поясніть нам, чому ви їм вірили.
Лаїв

Відповіді:


18

Пара причин:

  1. Як правило, коли ви спокушаєтесь перевірити приватний метод класу, це запах дизайну (клас айсберг, недостатньо публічних компонентів для багаторазового використання тощо). Майже завжди є якась «більша» проблема.

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

  3. Ви ризикуєте серйозним дублюванням тестів (тести, які виглядають / відчувають себе дуже подібними), перевіряючи приватні методи. Це має великі наслідки, коли вимоги змінюються, оскільки буде порушено набагато більше тестів, ніж необхідно. Це також може поставити вас у становище, коли важко переробити з-за вашого тестового набору ... що є остаточною іронією, адже тестовий набір є для того, щоб допомогти вам безпечно переробити та рефактор!

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

Коли перевірити приватні методи: Оскільки ця відповідь набула (дещо) популярності, я відчуваю, що я зобов'язаний зазначити, що "найкраща практика" - це завжди саме це: "найкраща практика". Це не означає, що ви повинні робити це догматично або наосліп. Якщо ви вважаєте, що ви повинні перевірити свої приватні методи та маєте законну причину (як, наприклад, ви пишете тести на характеристики для застарілої програми), то протестуйте свої приватні методи . Конкретні обставини завжди є важливим для будь-якого загального правила чи найкращої практики. Просто пам’ятайте про деякі речі, які можуть піти не так (див. Вище).

У мене є відповідь, яка детально переглядає це питання, яке я не повторю тут: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015


4
Причина 1: Неясна. Причина 2. Що робити, якщо ваш приватний помічник не повинен бути частиною загальнодоступного API? Причина 3: Не, якщо ви правильно розробили свій клас. Ваша остання порада: чому я видалив ідеально хороший тест, який підтверджує, що метод, який я написав, працює?
Роберт Харві

4
@RobertHarvey Причина 2: бути непрямим доступом через загальнодоступний API! = Бути частиною публічного API. Якщо ваша приватна функція не перевіряється через загальнодоступний API, то, можливо, це мертвий код і його слід видалити? Або ваш клас справді є айсбергом (причина 1), і його слід відновити.
Frax

5
@RobertHarvey, якщо ви не можете перевірити приватну функцію через загальнодоступний API, видаліть її, оскільки вона не корисна.
Девід Арно

1
@RobertHarvey 1: Дизайнерські запахи завжди дещо суб'єктивні / туманні, тому обов'язково. Але я перерахував декілька конкретних прикладів анти-зразків, і в моїй відповіді ТА є детальніше. 2: Приватні методи не можуть бути частиною публічного API (за визначенням: вони приватні) ... тому я не думаю, що ваше питання має особливий сенс. Я намагаюся домогтися того, що якщо у вас є щось на зразок bin_search(arr, item)(public) та bin_search(arr, item, low, hi)(private), існує багато способів пошуку бін), то все, що вам потрібно перевірити, - це громадськість, що стикається з одним ( bin_search(arr, item))
Метт Мессерсміт

1
@RobertHarvey 3: По-перше, я сказав ризик , а не гарантія . По-друге, стверджувати, що "це працює, якщо ти робиш це правильно" - це самореалізація. Наприклад, "Ви можете записати операційну систему в одній функції, якщо ви зробите це правильно ". Це неправда: але це також не корисно. Про підказку: Ви видалите її, тому що якщо ваша реалізація зміниться (тобто ви хочете поміняти приватний імпульс), то ваш тестовий набір увійде у ваш спосіб (у вас буде провальний тест, де ви не повинні).
Метт Мессерсміт

14

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


Не впевнений, чому ваша відповідь була оскаржена. Це коротко, до речі і отримує відповідь на 100% правильну.
Девід Арно

3
@DavidArno: Можливо, тому що тестування приватних методів насправді не має великого відношення до деталізації тесту. Це пов'язане з прив'язкою до деталей реалізації.
Роберт Харві

11

Написання одиничних тестів для приватних методів пов'язує ваші одиничні тести з детальними відомостями про впровадження.

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

Тим НЕ менше, чому , можливо , ви хочете , щоб писати тести для приватних методів?

Існує природна напруга між одиничними тестами та поступовим розвитком. Розробники програмного забезпечення, які використовують REPL (цикл читання-друку-друку), можуть засвідчити, наскільки продуктивним може бути швидко записувати та перевіряти невеликі шматочки функціональності під час "вирощування" класу чи функції. Єдиний хороший спосіб зробити це в середовищах, які керуються тестовими підрозділами, - це писати одиничні тести для приватних методів, але в цьому багато тертя. Для написання одиничних тестів потрібен час, для перевірки потрібен фактичний метод, і ваша тестова основа повинна підтримувати можливість зберігати метод приватним, щоб він не забруднював ваш зовнішній API.

У деяких екосистемах, таких як C # і .NET, є способи створення середовищ, подібних до REPL (такі інструменти, як Linqpad, роблять це), але їх корисність обмежена, оскільки у вас немає доступу до вашого проекту. Безпосереднє вікно у Visual Studio незручно; він все ще не має повного Intellisense, ви повинні використовувати в ньому повнокваліфіковані імена, і це запускає збірку кожного разу, коли ви його використовуєте.


4
@ user949300 Трохи важко це обговорити, не потрапляючи в жодну справжню помилковість шотландець , але є дуже багато погано написаних тестів усіх видів. З точки зору тестування одиниці, ви повинні перевірити державний контракт свого методу, не знаючи внутрішніх деталей реалізації. Не те, що стверджувати, що певна залежність називалася X разів, завжди неправильно: є ситуації, коли це має сенс. Вам просто потрібно переконатися, що це інформація, яку ви насправді хочете передати у контракті підрозділу, що перевіряється.
Вінсент Савард

3
@DavidArno: [знизую плечима] Я це робив уже деякий час. Тести підрозділів для приватних методів для мене завжди працювали чудово, доки Microsoft не вирішила припинити підтримувати проксі-об’єкти в їх тестовій основі. Ніколи в результаті нічого не вибухнуло. Я ніколи не виривав діру у Всесвіті, написавши тест для приватного методу.
Роберт Харві

2
@DavidArno: Чому я б кинув використовувати ідеально хороший прийом, який дає мені користь, лише тому, що хтось в Інтернеті каже, що це погана ідея, не надаючи жодних обґрунтування?
Роберт Харві

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

2
@Frax Звичайно, я міг би, але за цією логікою я повинен відмовитися від одиничних тестів на користь загальносистемних тестів на інтеграцію. Зрештою, "у більшості випадків ви повинні мати змогу модифікувати ці тести, щоб перевірити ту саму поведінку"
Олександр - Відновіть Моніку

6

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

Це призводить до кращого дотримання принципу єдиної відповідальності.


Це має бути прийнятою відповіддю.
Джек Едлі

0

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

Можливо, краще питання буде "Що мені робити, коли я хочу перевірити приватні методи?" , і відповідь начебто очевидна: ви повинні їх викрити таким чином, що робить можливим тестування. Тепер це не обов'язково означає, що вам слід просто оприлюднити метод, і це все. Швидше за все, ви захочете зробити більш високу абстракцію; перейдіть до іншої бібліотеки чи API, щоб ви могли робити свої тести в цій бібліотеці, не піддаючи цьому функціональності в своєму головному API.

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



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