Зробити приватний метод загальнодоступним для тестування його ... хороша ідея?


301

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


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

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

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

ОНОВЛЕННЯ

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

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


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


1
Не відповідь, але ви завжди можете отримати доступ до них за допомогою роздумів.
Зано

33
Ні, ви не повинні їх викривати. Натомість слід перевірити, що ваш клас поводиться так, як він повинен робити, через його публічний API. І якщо викриття внутрішніх справді є єдиним варіантом (у чому я сумніваюся), то вам слід принаймні зробити захищений пакет аксесуарів, щоб лише класи в тому ж пакеті (як і ваш тест має бути) доступ до них.
JB Nizet

4
Так. Цілком прийнятно надати видимість методу пакет-приватний (за замовчуванням), щоб зробити його доступним з одиничних тестів. Бібліотеки на кшталт Guava навіть надають @VisibileForTestingпримітку для виготовлення таких методів - я рекомендую зробити це так, щоб причина методу не була privateналежним чином задокументована.
Борис Павук

Відповіді:


186

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

Як загальне твердження, я, як правило, все перероблюю "виробничий" код, щоб полегшити тестування. Однак я не думаю, що це було б гарним дзвінком. Хороший одиничний тест (як правило) не повинен дбати про деталі реалізації класу, а лише про його видиму поведінку. Замість того, щоб виставляти внутрішні стеки тесту, ви можете перевірити, що клас повертає сторінки в тому порядку, який ви очікуєте після виклику first()або last().

Наприклад, розгляньте цей псевдо-код:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

8
Я повністю згоден з вашою відповіддю. Багато розробників спокушаються використовувати модифікатор доступу за замовчуванням і виправдовують, що хороші рамки з відкритим кодом використовують цю стратегію, тому вона повинна бути правильною. Просто тому, що ти не можеш означати, що ти повинен. +1
CKing

6
Хоча інші дали корисну інформацію, я мушу сказати, що це рішення, яке я вважаю найбільш відповідним цій проблемі. Він зберігає тести повністю агностичними щодо того, як досягається внутрішня цільова поведінка. +10!
Gitahi Ng'ang'a

До речі, чи добре, що у прикладі, поданому тут @Mureinik, методи тестування в кінцевому підсумку охоплюють більше, ніж власне тестований метод? Наприклад, testFirst () викликає next (), що насправді не є методом, який перевіряється тут, first () є. Чи не було б чистіше і зрозуміліше мати лише 1 метод випробування, що охоплює всі 4 методи навігації?
Gitahi Ng'ang'a

1
Хоча це ідеал, буває, що вам потрібно перевірити, чи компонент зробив щось правильне, а не лише те, що було успішно. Це може бути складніше для чорного тесту. Десь вам доведеться перевірити компонент у білій коробці.
Пітер Лорі

1
Створення геттерів лише заради тестування одиниць? Це, безумовно, суперечить зерну OOP / Об'єктного мислення в тому, що інтерфейс повинен викривати поведінку, а не дані.
IntelliData

151

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

Якщо ви дійсно хочете перевірити приватний метод ізольовано, на Java ви можете використовувати Easymock / Powermock, щоб дозволити це зробити.

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

" Слухайте тести " - якщо це важко перевірити, чи це щось вам розповідає про ваш дизайн? Чи не могли б ви змінити те, де тест цього методу був би тривіальним і легко покривався б тестуванням через загальнодоступні програми?

Ось що Майкл Пірс має сказати у " Ефективній роботі зі спадковим кодексом "

"Багато людей витрачають багато часу, намагаючись вирішити, як подолати цю проблему ... справжня відповідь полягає в тому, що якщо у вас є бажання протестувати приватний метод, метод не повинен бути приватним; якщо зробити метод відкритим Ви турбуєте вас, швидше за все, це тому, що це частина окремої відповідальності; вона повинна бути на іншому класі ". [ Ефективна робота зі спадковим кодексом (2005) М. Feathers]


5
+1 для Easymock / Powermock і ніколи не публікуйте приватний метод
Леонард Брюнінгз

51
+1 За "Слухайте тести". Якщо ви відчуваєте потребу в тестуванні приватного методу, є ймовірність, що логіка в цьому методі настільки складна, що повинна знаходитися в окремому класі помічників. Тоді ви можете протестувати клас помічників.
Філ

2
"Слухати тести", здається, знижується. Ось кешоване посилання: webcache.googleusercontent.com/…
Джесс Телфорд

1
Я погоджуюся з @Phil (особливо довідник про книгу), але я часто опиняюся в ситуації з куркою / яйцями зі спадковим кодом. Я знаю, що цей метод належить до іншого класу, проте в першу чергу мені не комфортно рефакторинг без тестів.
Кевін

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

67

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

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


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

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

@mal У мене був би тест на співпрацю. Іншими словами, макет щодо перевірки нового класу був викликаний правильно. Дивіться мою відповідь нижче.
Фінглас

Чи можна написати тестовий клас, який успадковується від класу з внутрішньою логікою, яку ви хочете протестувати, і надати відкритий метод, який використовується для тестування внутрішньої логіки?
sholsinger

1
@sholsinger: у C # публічний клас може не успадковувати внутрішній клас.
Ерік Ліпперт

45

Багато відповідей пропонують протестувати лише загальнодоступний інтерфейс, але IMHO це нереально - якщо метод робить щось, що займає 5 кроків, ви хочете перевірити ці п’ять кроків окремо, а не всі разом. Для цього потрібно протестувати всі п’ять методів, які (крім тестування) можуть бути інакше private.

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

Так, це призведе до розширення файлів та класів.

Так, це робить publicі privateспецифікатори зайвими.

Так, це біль у попі.

Це, на жаль, одна з багатьох жертв, які ми робимо, щоб зробити код перевіряючим . Можливо, майбутня мова (або навіть майбутня версія C # / Java) матиме можливості зробити зручнішими тестування класів та модулів; але тим часом нам доведеться стрибати через ці обручі.


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


2
приємна відповідь для 1-ї половини
cmcginty

7
+1: найкраща відповідь для мене. Якщо виробник авто не перевірить частину свого автомобіля з тієї ж причини, я не впевнений, що хтось його купить. Важливу частину коду слід перевірити, навіть якщо ми повинні змінити його на загальнодоступний.
Tae-Sung Shin

1
Дуже тонка відповідь. У вас мій теплий голос. Я додав відповідь. Це може вас зацікавити.
davidxxx

29

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


+1 і realted Відповідь: stackoverflow.com/questions/4603847 / ...
Raedwald

Afair, рідкісна ситуація, яку я коли-небудь вирішив використовувати приватних осіб у тестовій справі, коли перевіряв правильність об'єкта - якщо він закрив усі обробники JNI. Очевидно, що він не піддається впливу публічного API, однак, якщо цього не відбудеться, відбувається витік пам'яті. Але смішно, пізніше я його також позбувся після того, як ввів детектор витоку пам’яті як частина публічного API.
кан

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

6
@Casey: якщо приватний метод не використовується публічним інтерфейсом, чому він існує?
Тило

2
@DaSilva_I Ireland: В майбутньому буде складно підтримувати тестові справи. Єдиний реальний випадок використання класу - це публічний метод. Тобто весь інший код може використовувати клас лише через публічний API. Отже, якщо ви хочете змінити тестовий випадок впровадження та приватного методу, це не означає, що ви робите щось не так, більше того, якщо ви протестуєте лише приватне, але не тестуєте загальнодоступне, воно не охопить деякі потенційні помилки . В ідеалі тестовий випадок повинен охоплювати всі можливі випадки та лише випадки реального використання класу.
кан

22

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

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


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

20

Як щодо того, щоб зробити його пакет приватним? Тоді ваш тестовий код може бачити його (а також інші класи у вашому пакеті), але він все ще прихований від ваших користувачів.

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

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


+1, якщо вам потрібно перевірити своїх приватних осіб, вам, ймовірно, не вистачає класу
jk.

+1 для відсутнього класу. Радий бачити, що інші не стрибають на "маркуй внутрішній" смузі прямо.
Finglas

Хлопчик, мені довелося пройти довгий шлях, щоб знайти приватну відповідь пакету.
Білл К

17

Оновлення : я додав більш розгорнуту і більш повну відповідь на це питання в багатьох інших місцях. Це можна знайти в моєму блозі .

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

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

Наприклад:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

Refactor це шляхом введення об’єкта валідатора.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

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


1
Я особисто думаю, що SRP можна зайняти занадто далеко, наприклад, якщо я напишу службу облікового запису, яка займається оновленням / створенням / видаленням облікових записів у додатку, ти мені кажеш, що ти повинен створити окремий клас для кожної операції? А як щодо валідації, наприклад, чи дійсно варто вилучити логіку перевірки до іншого класу, коли вона ніколи більше не буде використовуватися? imo, що просто робить код менш читабельним, менш стислим і пакує вашу програму із зайвими класами!
jcvandan

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

1
Щодо того, щоб зробити ваш код менш читабельним, я категорично не згоден. Наявність класу для перевірки легше прочитати, ніж те, щоб перевірка була вбудована в інший об'єкт. У вас ще однакова кількість коду, він просто не вкладається в один великий клас. Я вважаю за краще кілька класів, які дуже добре роблять одну справу, ніж один великий клас.
Фінглас

4
@dormisher, стосовно послуги вашого облікового запису, вважайте SRP як "єдину причину змін". Якщо ваші операції з CRUD природно змінюватимуться разом, вони відповідатимуть SRP. Зазвичай ви можете змінити їх, оскільки схема бази даних може змінитися, що, природно, вплине на вставлення та оновлення (хоча, можливо, не завжди видаляється).
Ентоні Пеграм

@finglas, що ви думаєте з цього приводу: stackoverflow.com/questions/29354729/… . Я не відчуваю тестування приватного методу, тому, можливо, я не повинен розділяти його на клас, але він використовується у двох інших публічних методах, тому я б у кінцевому підсумку тестував двічі ту саму логіку.
Родріго Руїз

13

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


11

Чому б не розділити алгоритм управління стеком на клас корисності? Клас корисності може керувати стеками та надавати загальнодоступні користувачі. Його одиничні тести можуть бути зосереджені на деталях впровадження. Глибокі тести для алгоритмічно складних занять дуже корисні для складання крайових справ та забезпечення покриття.

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


10

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


10

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

У вас є пара варіантів, якщо ви хочете перевірити їх.

  • Використовуйте рефлексію
  • Надати доступ пакету методів

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

Тут є дуже гарна стаття .


1
Справедливий коментар. Це був лише варіант, можливо, поганий.
адаммаркхам

@Finglas Чому тестування приватного коду за допомогою відображення є поганою ідеєю?
Аншул

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

1
@Finglas Чому ви не хочете перевірити деталі реалізації? Це код, який повинен працювати, і якщо він змінюється частіше, ви очікуєте, що він буде порушуватися частіше, що є більшою підставою перевірити його більше, ніж публічний API. Коли ваша база коду буде стабільною, скажімо, колись у майбутньому хтось підійде і змінить приватний метод, який порушує роботу вашого класу всередині. Тест, який тестує внутрішніх справ вашого класу, негайно скаже їм, що вони щось зламали. Так, це ускладнить підтримку ваших тестів, але це те, чого ви очікуєте від тестування повного покриття.
Аншул

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

9

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

Потім додайте атрибут dll

[складання: InternalsVisibleTo ("MyTestAssembly")]

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


7

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

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


Що б ви зробили з методом недійсності?
IntelliData

@IntelliData Використовуйте відображення для доступу до приватних змінних, якщо вам потрібно.
Даніель Алексюк

6

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

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


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

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

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

@dormisher - Якщо клас коли-небудь використовується лише через інтерфейс, вам потрібно лише перевірити, чи правильно (а) методи публічного інтерфейсу ведуть себе правильно. Якщо публічні методи роблять те, що вони мають на меті робити, чому ви дбаєте про приватні?
Анджей Дойл

@Andrzej - Я вважаю, що насправді не слід, але є ситуації, коли я вважаю, що це може бути дійсним, наприклад, приватний метод, який перевіряє стан моделі, можливо, варто перевірити такий метод, але такий спосіб, як це, слід перевірити через публічний інтерфейс у будь-якому випадку
jcvandan

6

з точки зору одиничного тестування, ви точно не повинні додавати більше методів; Я вважаю, що вам краще скласти тестовий випадок саме щодо вашого first()методу, який би називався перед кожним тестом; то ви можете зателефонувати в кілька разів більше - next(), previous()і last()щоб побачити , якщо результати відповідали вашим очікуванням. Я думаю, якщо ви не додасте більше методів до свого класу (лише для цілей тестування), ви б дотримувались принципу "чорної скриньки" тестування;



5

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

  1. Тестування чорної скриньки

    Школа чорної скриньки говорить, що клас слід розглядати як чорний ящик, щоб ніхто не бачив реалізації всередині нього. Єдиний спосіб перевірити це через загальнодоступний API - так, як користувач класу буде ним користуватися.

  2. тестування білого поля.

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

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


4

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


4

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

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


4

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

Натомість слід "перевірити вище". Перевірте метод, який викликає приватний метод. Але ваші тести повинні перевіряти потреби ваших програм, а не ваші "рішення щодо впровадження".

Наприклад (псевдокод боду тут);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

Немає підстав для тестування "додати", ви можете замість цього випробувати "книги".

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


1
"Ніколи не дозволяйте вашим тестам приймати рішення щодо дизайну коду для вас" - я повинен погодитися. Важко перевірити - кодовий запах. Якщо ви не можете перевірити його, то як ви знаєте, що він не зламаний?
Альфред Армстронг

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

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

Це рішення підходить для такого методу, як книги, які повертають значення - ви можете перевірити тип повернення в асперті ; але як би ви поставились до перевірки недійсного методу?
IntelliData

недійсні методи щось роблять або їм не потрібно існувати. Перевірте те, що вони роблять,
coteyr

2

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

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


2

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

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

Це, звичайно, стосується лише частини C # / .NET вашого питання


2

Я часто додати метод під назвою що - щось на зразок validate, verify, checkі т.д., до класу так , що його можна назвати , щоб перевірити внутрішній стан об'єкта.

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


2

У Guava є анотація @VisibleForTesting для методів маркування, які мають розширений обсяг (пакетний або загальнодоступний), що вони інакше. Я використовую примітку @Private для того ж самого.

Хоча загальнодоступний API повинен бути протестований, іноді зручно та розумно звертатися до речей, які зазвичай не є загальнодоступними.

Коли:

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

здається, що релігія трубила інженерію.


2

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


2

Ні, тому що є кращі способи зняти шкіру з цією кішкою.

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

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

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

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


2

У .Net існує спеціальний клас, який називається PrivateObject розроблений спеціально для доступу до приватних методів класу.

Докладніше про це можна дізнатися на MSDN або тут на стеку Overflow

(Мені цікаво, що поки ніхто про це не згадував.)

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

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


1

Як широко відмічено в коментарях інших, одиничні тести повинні зосереджуватися на публічному API. Однак плюси та мінуси та обґрунтування убік, ви можете викликати приватні методи в одиничному тесті, використовуючи рефлексію. Звичайно, вам потрібно буде переконатися, що ваша безпека JRE дозволяє це. Виклик приватних методів - це те, що використовує Spring Framework, використовуючи його ReflectionUtils (див. makeAccessible(Method)Метод).

Ось невеликий приклад клас із приватним методом екземпляра.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

І приклад класу, який виконує метод приватного примірника.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

Виконання B надрукує Doing something private. Якщо вам це справді потрібно, відображення може бути використане в одиничних тестах для доступу до приватних методів екземпляра.


0

Особисто у мене є ті ж проблеми при тестуванні приватних методів, і це тому, що деякі інструменти тестування обмежені. Не добре, щоб ваш дизайн керувався обмеженими інструментами, якщо вони не відповідають вашим потребам, змінюють інструмент, а не дизайн. Оскільки ваш запит на C # я не можу запропонувати хороші інструменти тестування, але для Java є два потужні інструменти: TestNG та PowerMock, і ви можете знайти відповідні інструменти тестування для платформи .NET

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