Як саме слід писати одиничні тести, не надмірно глузуючи?


80

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

  1. Вони не повинні порушуватися будь-якою незв'язаною зміною коду в іншому місці бази даних.
  2. Помилка в тестованому блоці повинна бути зламана лише одна одиниця тесту на відміну від інтеграційних тестів (які можуть зламатися у купи).

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

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

Тепер до питання (ів).

  1. Як правильно писати одиничні тести?
  2. Де саме лежить лінія між ними та інтеграційними тестами?

Оновлення 1

Будь ласка, врахуйте наступний псевдо-код:

class Person {
    constructor(calculator) {}

    calculate(a, b) {
        const sum = this.calculator.add(a, b);

        // do some other stuff with the `sum`
    }
}

Чи може тест, який тестує Person.calculateметод, не знущаючись над Calculatorзалежністю (враховуючи, що Calculatorце легкий клас, який не має доступу до "зовнішнього світу"), вважати одиничним тестом?


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

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

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

13
@Michael Просто сказати "так, так" і оголосити протилежний погляд неправильним - не чудовий спосіб підійти до такого спірного предмету. Можливо, напишіть відповідь і уточнюйте, чому ви вважаєте, що глузування повинно бути скрізь, і, можливо, чому ви вважаєте, «тонни статей» по суті неправильні?
AJFaraday

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

Відповіді:


59

Суть одиничних тестів полягає у випробуванні одиниць коду ізольовано.

Мартін Фаулер на блок-тесті

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

Про те, що Кент Бек писав у « Тестовій керованій розробці», на прикладі

Я називаю їх "одиничними тестами", але вони не дуже відповідають прийнятому визначенню одиничних тестів

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

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

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

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

Ви можете порівняти:

Чи може тест, який тестує метод Person.calculate, не знущаючись із залежності від калькулятора (враховуючи, що Калькулятор - це легкий клас, який не має доступу до "зовнішнього світу"), вважати одиничним тестом?

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

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

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

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

Дивіться також " Гаряча лава " Гаррі Персіваль.


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

40

Як саме слід писати одиничні тести, не надмірно глузуючи?

За рахунок мінімізації побічних ефектів у вашому коді.

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

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

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


8
Це правильна відповідь як на практиці, так і з точки зору ухилення від безглуздих смислових дискусій.
Джаред Сміт

Чи є у вас приклад нетривіальної бази коду з відкритим кодом, яка використовує такий стиль і все ще отримує хороше тестове покриття?
Joeri Sebrechts

4
@JoeriSebrechts кожен FP один? Приклад
Джаред Сміт

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

1
@JoeriSebrechts Хм, або я нерозумію те, що ти хочеш, або ти не копався досить глибоко в прикладі, який я дав. Функції ramda використовують внутрішні дзвінки повсюдно у своєму джерелі (наприклад R.equals). Оскільки це здебільшого чисті функції, вони, як правило, не викриваються в тестах.
Джаред Сміт

31

Ці питання досить різні за своєю складністю. Візьмемо спочатку питання 2.

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

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

Багато практикуючих побоюються, що якщо тест B повторно використовує класи, які вже були протестовані за допомогою тесту А, то дефект у блоці А спричиняє збої в тесті в декількох місцях. Я вважаю , що це не проблема: а тестовий набір повинен досягти успіху 100% для того , щоб дати вам впевненість , що потрібно, так що це не велика проблема , щоб мати занадто багато невдач - в кінці кінців, ви робите є дефект. Єдиною критичною проблемою було б, якщо дефект спричинив занадто мало відмов.

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


4
The only critical problem would be if a defect triggered too few failures.це одна із слабких сторін глузування. Ми мусимо «запрограмувати» очікувану поведінку, щоб ми не змогли зробити це, в результаті чого наші тести закінчилися як «помилкові позитиви». Але глузування - дуже корисна техніка для досягнення детермінізму (найважливіша умова тестування). Я використовую їх у всіх своїх проектах, коли це можливо. Вони також показують мені, коли інтеграція занадто складна або залежність занадто жорстка.
Лаїв

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

11
@AlexanderLomia: Що б ви назвали підрозділом? Ви б також назвали "Рядок" одиницею? Я б хотів, але не мріяв би знущатися над цим.
Барт ван Інген Шенау

5
" Одиничні тести та інтеграційні тести чітко розділені. Одиничний тест тестує одну одиницю (метод або клас) і використовує інші підрозділи лише стільки, скільки потрібно для досягнення цієї мети ". Ось руб. Це ваше визначення одиничного тесту. Моя зовсім інша. Отже, відмінність між ними лише "чітко відокремлена" для будь-якого даного визначення, але поділ змінюється між визначеннями.
Девід Арно

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

6

Добре, тож відповісти на ваші запитання безпосередньо:

Як правильно писати одиничні тести?

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

Де саме лежить лінія між ними та інтеграційні тести?

Інтеграційний тест - це одиничний тест, коли ваші залежності не висміюються.

Чи може тест, який тестує метод Person.calculate, не знущаючись з калькулятора, вважати одиничним тестом?

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

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

Але ваше справжнє питання здається таким:

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

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

public class MockCalc : ICalculator
{
     public Add(int a, int b) { return 4; }
}

Я б не робив чогось такого:

myMock = Mock<ICalculator>().Add((a,b) => {return a + b;})
myPerson.Calculate()
Assert.WasCalled(myMock.Add());

Я заперечую, що це було б "тестування моєї макети" або "тестування реалізації". Я б сказав: " Не пишіть знущань! * Так".

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


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

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

Я б перейменував ваш клас MockCalcу StubCalc, і назвав би це заглушкою, а не глумливим. martinfowler.com/articles/…
bdsl

@bdsl Цій статті 15 років
Еван

4
  1. Як слід правильно виконувати одиничні тести?

Моє правило полягає в тому, що правильні одиничні тести:

  • Кодуються інтерфейсами, а не реалізаціями . Це має багато переваг. Для одного це гарантує, що ваші класи керуються принципом інверсії залежності від SOLID . Також це роблять ваші інші класи ( правда? ), Тому ваші тести повинні робити те саме. Крім того, це дозволяє протестувати декілька реалізацій одного інтерфейсу, використовуючи при цьому велику частину тестового коду (змінилися б лише ініціалізація та деякі твердження).
  • Є самодостатніми . Як ви вже говорили, зміни будь-якого зовнішнього коду не можуть вплинути на результат тесту. Таким чином, одиничні тести можуть виконуватися під час збирання. Це означає, що вам потрібні глузування, щоб усунути будь-які побічні ефекти. Однак якщо ви дотримуєтесь принципу інверсії залежності, це має бути відносно просто. Хороші тестові рамки, такі як Spock, можуть бути використані для динамічного забезпечення макетних реалізацій будь-якого інтерфейсу для використання у ваших тестах з мінімальним кодуванням. Це означає, що кожному тестовому класу потрібно лише виконати код з точно одного класу реалізації, плюс тестовий фреймворк (і, можливо, модельні класи ["квасоля"]).
  • Не потрібно окремого запущеного додатка . Якщо тест потребує "спілкування з чимось", будь то база даних чи веб-сервіс, це інтеграційний тест, а не одиничний тест. Я малюю лінію на мережевих з'єднаннях або у файловій системі. Наприклад, суто вбудована в пам'ять база даних SQLite - це, на мою думку, чесна гра для одиничного тесту, якщо вона вам справді потрібна.

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

  1. Де саме лежить лінія між ними [одиничні тести] та інтеграційні тести?

Я визнав цю відмінність найбільш корисною:

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

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

  1. Чи правда, що практично кожен одиничний тест потрібно знущатися?

Ні. Деякі окремі тестові випадки стосуватимуться умов помилок, наприклад, проходження nullв якості параметра та підтвердження отримання винятком. Багато подібних тестів не потребуватимуть ніяких глузувань. Крім того, реалізація, яка не має побічних ефектів, наприклад обробка рядків або математичні функції, може не вимагати ніяких макетів, оскільки ви просто перевіряєте вихід. Але, на мою думку, для більшості класів, як я думаю, потрібен хоча б один макет десь у тестовому коді. (Чим менше, тим краще.)

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

Лише одна одиниця тесту повинна бути зламана помилкою в тестованому пристрої.

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


3
  1. Вони не повинні порушуватися будь-якою незв'язаною зміною коду в іншому місці бази даних.

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

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

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

Де саме лежить лінія між ними та інтеграційні тести?

Я не думаю, що це важлива відмінність. Що таке "одиниця" коду?

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

Як саме слід писати одиничні тести, не надмірно глузуючи?

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


" Що таке" одиниця "коду так чи інакше? ", Дуже гарне запитання, яке може мати несподівані відповіді, які можуть залежати навіть від того, хто відповідає. Як правило, більшість визначень одиничних тестів пояснюють їх такими, що стосуються методу чи класу, але це не дуже корисна міра "одиниці" у всіх випадках. Якщо у мене є Person:tellStory()метод, який включає в себе деталі людини в рядок, то повертає це, то "історія", ймовірно, одна одиниця. Якщо я роблю приватний помічник, який витягує частину коду, я не вірю, що я ввів новий блок - мені не потрібно тестувати це окремо.
ВЛАЗ

1

По-перше, деякі визначення:

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

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

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

Код пахне

Для цього ми звернемося до Вікіпедії:

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

Це продовжується пізніше ...

"Запахи - це певні структури в коді, які вказують на порушення основних принципів дизайну і негативно впливають на якість дизайну". Сурянараяна, Гіріш (листопад 2014 р.). Рефакторинг для програмного забезпечення запахів. Морган Кауфман. p. 258.

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

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

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

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

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

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

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


0

Знущання слід використовувати лише в крайньому випадку, навіть в одиничних тестах.

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

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

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

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

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