Занадто багато запаху коду?


19

Я дійсно закохався в тестування одиниць та TDD - я заражений.

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

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

Однак я помітив, що я, як правило, використовую твердження в публічних методах (як і приватних) просто «щоб бути впевненим». Чи може це бути "тестування дублювання", оскільки припущення публічного методу випробовуються ззовні одиничною рамкою тестування?

Невже хтось може подумати надто багато тверджень як запах коду?


1
чи ці твердження увімкнено чи вимкнено у випуску?
jk.

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

Щойно знайшли це "Стверджує підвищення продуктивності, відстежуючи помилки" programmers.stackexchange.com/a/16317/32342
Florents Tselai

як ви визначаєте "занадто багато"?
haylem

@haylem Існує "занадто багато" тверджень, якщо інший програміст читає код і каже "Я втомився від цих тверджень".
Флоренц Целай

Відповіді:


26

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

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


2
Дуже хороший момент!
Флоренц Целай

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

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

2
Твердження в коді для мене доповнюють твердження одиничного тестування. Ви не будете мати 100% тестового покриття, і твердження в коді допоможуть швидше звузити невдалі тестові одиниці.
sebastiangeiger

15

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

Робити DbC - це гарна ідея, але вам слід принаймні розглянути можливість переходу на мову, яка підтримує її на самому собі (наприклад, Ейфель ) або хоча б використовувати рамку контракту для вашої платформи (наприклад, Microsoft Code Contracts for .NETце дуже приємно, можливо, найскладніша рамка контракту, навіть потужніша за Ейфеля). Таким чином, ви можете краще використовувати силу контрактів. Наприклад, використовуючи існуючий фреймворк, ви можете обробляти контракти у вашій документації або IDE (наприклад, Контракти на коди для .NET відображаються в IntelliSense і, якщо у вас є VS Ultimate, можна навіть статично перевірити автоматичним підтвердженням теореми під час компіляції, поки Ви також вводите; так само у багатьох контрактних рамках Java є доклети JavaDoc, які автоматично витягують контракти у Вашу документацію API JavaDoc).

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

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


4

Кожен раз, коли ви не маєте повного контролю над своїми вхідними параметрами, дуже гарна ідея перевірити фронт на наявність простих помилок. Наприклад, помилка на нуль.

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

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

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


1
+1 Для "Мені не подобається твердження про затвердження в Java, оскільки їх можна вимкнути, і тоді це помилкова безпека".
Флоренц Целай

3

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

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

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

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


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

Твердження можуть (і в багатьох випадках повинні) включати реєстрацію та викидання виключень (або коди помилок у C--).
Денні Варод

3

Важко бути мовно-агностичним щодо цього питання, оскільки деталі щодо того, як реалізуються твердження та "правильне" поводження з помилками / винятками, стосуються відповіді. Ось мій 0,02 дол. США, заснований на моїх знаннях Java & C ++.

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

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

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


2

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

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

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


0

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

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

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

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


0

Однак одиничне тестування використовується для публічних методів.

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

Невже хтось може подумати надто багато тверджень як запах коду?

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


-2

Це погана практика.

Не слід перевіряти публічні / приватні методи. Ви повинні перевірити клас в цілому. Просто переконайтеся, що у вас хороше покриття.

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

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

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


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

Також щодо "це говорить про те, що автор коду насправді не знав, що він робить [...] ви робите". Майбутнім розробникам може бути не так зрозуміло, що робить конкретний метод. У цьому випадку наявність умов до і після публікації у вигляді тверджень може бути неоціненним для розуміння фрагмента коду.
Олексі

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