Як уникнути логічних помилок у коді, коли TDD не допомогла?


67

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

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

Ось відповідний фрагмент коду:

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return _number_to_text(delta) + " days ago"

if delta < 30:
    weeks = math.floor(delta / 7)
    if weeks == 1:
        return "A week ago"

    return _number_to_text(weeks) + " weeks ago"

if delta < 365:
    ... # Handle months and years in similar manner.

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

Що я пропустив, це те, що подія може статися позавчора, будучи один день тому: наприклад, подія, яка відбулася двадцять шість годин тому, була б один день тому, а не вчора, якщо зараз - 1 ранку. Точніше, це одна точка щось, але оскільки deltaціле число, воно буде лише одним. У цьому випадку програма відображає "Один день тому", що, очевидно, несподівано і не оброблене в коді. Це можна виправити, додавши:

if delta == 1:
    return "A day ago"

відразу після обчислення delta.

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

  • Зробити логічну помилку дуже просто навіть у такому простому вихідному коді.
  • Тестова розробка не допомогла.

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

Як я міг у першу чергу уникнути створення цієї помилки?


38
Маючи для цього тестовий випадок? Це схоже на те, як ви виявили це згодом, і з'єднуєтеся з TDD.
Οurous

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

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

43
Вітаємо, ви знову виявили стару мудрість, що жоден тест не може довести відсутність помилок. Але якщо ви шукаєте методи для кращого охоплення можливого вхідного домену, вам потрібно зробити ретельний аналіз домену, крайових випадків та класів еквівалентності цього домену. Усі старі, добре відомі методи, давно відомі до того, як був винайдений термін TDD.
Док Браун

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

Відповіді:


57

Це типи помилок, які ви зазвичай виявляєте на кроці рефактора червоного / зеленого / рефактора. Не забувайте про цей крок! Розгляньте такий рефактор, як наступний (неперевірений):

def pluralize(num, unit):
    if num == 1:
        return unit
    else:
        return unit + "s"

def convert_to_unit(delta, unit):
    factor = 1
    if unit == "week":
        factor = 7 
    elif unit == "month":
        factor = 30
    elif unit == "year":
        factor = 365
    return delta // factor

def best_unit(delta):
    if delta < 7:
        return "day"
    elif delta < 30:
        return "week"
    elif delta < 365:
        return "month"
    else:
        return "year"

def human_friendly(event_date):
    date = event_date.date()
    today = now.date()
    yesterday = today - datetime.timedelta(1)
    if date == today:
        return "Today"
    elif date == yesterday:
        return "Yesterday"
    else:
        delta = (now - event_date).days
        unit = best_unit(delta)
        converted = convert_to_unit(delta, unit)
        pluralized = pluralize(converted, unit)
        return "{} {} ago".format(converted, pluralized)

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

Інші більш витончені тестові випадки також легше приходять до тями, дивлячись на такий тип реконструкції. Наприклад, що best_unitробити, якщо deltaце негативно?

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


12
Наступним кроком є ​​інтернаціоналізація, і pluralizeлише робота над підмножиною англійських слів несе відповідальність.
Дедуплікатор

@Deduplicator впевнений, але тоді залежно від того, на які мови / культури ви орієнтовані, ви можете уникнути лише модифікації, pluralizeвикористовуючи numта unitстворити якийсь ключ, щоб витягнути рядок формату з якогось файлу таблиці / ресурсу. АБО вам може знадобитися повне переписання логіки, тому що вам потрібні різні одиниці ;-)
Халк

4
Проблема залишається навіть із цією рефакторизацією, яка полягає в тому, що "вчорашній день" не має особливого сенсу в самі понеділкові години ранку (незабаром після 00:01). По-людськи, щось, що трапилося о 23:59, раптом не змінюється з "сьогодні" на "вчора", коли годинник просувається минулої півночі. Він замість цього змінюється з "1 хвилину тому" на "2 хвилини тому". "Сьогодні" занадто грубо в плані чогось, що трапилося, але хвилини тому, а "вчора" загрожує проблемами нічних сов.
Девід Хаммен

@DavidHammen Це питання зручності використання, і це залежить від того, наскільки точним потрібно бути. Коли ви хочете знати хоча б до години, я б не вважав, що "вчора" це добре. "24 години тому" набагато зрозуміліше і є загальновживаним людським виразом, щоб підкреслити кількість годин. Комп'ютери, які намагаються бути "людьми дружніми", майже завжди отримують це неправильно і занадто узагальнюють його до "вчорашнього", що є занадто розпливчастим. Але для того, щоб знати це, вам потрібно буде опитати користувачів, щоб побачити, що вони думають. Для деяких речей ви дійсно хочете точної дати та часу, тому "вчора" завжди помиляється.
Брандін

149

Тестова розробка не допомогла.

Здається, що це допомогло, просто те, що у вас не було тесту на сценарій "день тому". Імовірно, ви додали тест після виявлення цієї справи; це все ще TDD, оскільки, коли виявлені помилки, ви пишете блок-тест, щоб виявити помилку, а потім виправите її.

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


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

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

15
TDD так само хороший, як і тести, написані.
Міндвін

Ще одне зауваження: додавання тесту для цього випадку покращить дизайн, змусивши нас вийняти це datetime.utcnow()з функції та замість цього передати nowяк (відтворюваний) аргумент.
Toby Speight

114

подія, що сталася двадцять шість годин тому, була б один день тому

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


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

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

1
Мені подобається ця відповідь, оскільки вона вказує на справжню проблему: моменти часу та дати - це дві різні величини. Вони пов'язані, але коли ви починаєте порівнювати їх, речі швидко йдуть на південь. У програмуванні логіка дати та часу - це одне з найскладніших речей, що підходять правильно. Мені дуже не подобається, що багато втілень у даті в основному зберігають дату як 0:00. Це викликає багато плутанини.
Пітер Б

38

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

Пов’язане читання: Чи можна досягти абсолютного нульового стану помилки для програм великого масштабу?


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

35

Зазвичай я вважаю, що я можу допомогти.

По-перше, я шукаю крайні корпуси. Це місця, де змінюється поведінка. У вашому випадку поведінка змінюється в декількох точках уздовж послідовності цілих цілих днів. Існує крайовий регістр у нулі, на одиниці, на сім і т. Д. Я тоді писав би тестові випадки в крайових та навколо них. У мене були б тестові справи за -1 день, 0 днів, 1 годину, 23 години, 24 години, 25 годин, 6 днів, 7 днів, 8 днів тощо.

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


9
Це дійсно важлива частина TDD, яку часто не помічають, і я рідко бачив, про яку говорили в статтях та посібниках - це дуже важливо перевірити крайові випадки та граничні умови, оскільки я вважаю, що це джерело 90% помилок - від -одні помилки, переповнення та переливання, останній день місяця, останній місяць року, високосні роки тощо тощо
GoatInTheMachine

2
@GoatInTheMachine - і 90% цих 90% помилок - це навколо переходу на літній час ..... Хахаха
Калеб

1
Спочатку можна розділити можливі входи на класи еквівалентності, а потім визначити крайові регістри на межах класів. На наш погляд, це зусилля, які можуть бути більшими, ніж зусилля з розвитку; чи варто цього, залежить від того, наскільки важливо поставляти програмне забезпечення якнайбільше помилок, який термін і скільки грошей та терпіння у вас є.
Пітер А. Шнайдер

2
Це правильна відповідь. Багато правил бізнесу вимагають від вас розділити діапазон значень на інтервали, де їх випадки обробляються різними способами.
abuzittin gillifirca

14

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

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

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

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

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

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

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

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


12

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

Чому ні? Це звучить як досить гарна ідея!

Додавання до коду контрактів (тверджень) є досить надійним способом поліпшення його коректності. Як правило, ми додаємо їх як передумови при введенні функції та постумови при поверненні функції. Наприклад, ми могли б додати постусловіем , що все повернені значення є або форми «A [одиниці] назад» або «[число] [одиниця виміру] S назад». Якщо це зроблено дисципліновано, це призводить до дизайну за контрактом , і це один з найпоширеніших способів написання коду з високою впевненістю.

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

У PBT замість тестування на конкретні виходи коду ви перевіряєте, що вихід відповідає одній властивості. Наприклад, одна властивості reverse()функції є те , що для будь-якого списку l, reverse(reverse(l)) = l. Переважно написання тестів на кшталт цього, ви можете змусити PBT-механізм генерувати кілька сотень довільних списків (і декілька патологічних) і перевірити, чи всі вони мають цю властивість. Якщо цього немає , двигун "стискає" невдалий випадок, щоб знайти мінімальний список, який порушує ваш код. Схоже, ви пишете Python, який має гіпотезу як основну структуру PBT.

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


2
Це саме правильне рішення подібної проблеми. Набір дійсних результатів легко визначити (ви можете дати регулярний вираз дуже просто, щось на кшталт /(today)|(yesterday)|([2-6] days ago)|...), і тоді ви можете запустити процес з випадково вибраними входами, поки не знайдете той, який не знаходиться в наборі очікуваних результатів. Такий підхід сприйняв би цю помилку, і не вимагає усвідомлення того, що помилка може існувати заздалегідь.
Жуль

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

1
Якщо ви зробите так багато циклу в тестах, якщо це займе дуже багато часу, це переможе одну з головних цілей одиничного тестування: запускайте тести швидко !
CJ Dennis

5

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

def time_ago(delta, unit):
    delta_str = _number_to_text(delta) + " " + unit;
    if delta == 1:
        return delta_str + " ago"
    else:
        return delta_str = "s ago"

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return time_ago(delta, "day")

if delta < 30:
    weeks = math.floor(delta / 7)
    return time_ago(weeks, "week")

if delta < 365:
    months = math.floor(delta / 31)
    return time_ago(months, "month")

5

Тестова розробка не допомогла.

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

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

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

Основна методика написання надійного програмного забезпечення - це також основна методика розуміння того, як писати ефективні тести:

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

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


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


1

Це один з найважливіших фактів розробки програмного забезпечення: писати код без помилок абсолютно неможливо.

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

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

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

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

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


1

Ви просто не думали про цю справу раніше, і тому не мали для неї тестового випадку.

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

На автопілот літака ви витратили б набагато більше часу, ніж на простий інструмент.

Це часто допомагає подумати про допустимі діапазони вхідних змінних і перевірити ці межі.

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


1

(і вважаючи, що це стосується часових поясів, незважаючи на рівномірне використання UTC у коді)

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

Приклад: в Австралії подія трапляється о 9 ранку за місцевим часом. О 11 ранку він буде відображатися як "вчора", оскільки дата UTC змінилася.


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

  • Якщо можливо, введіть тестові приклади як колекції. Це робить додавання ще одного тесту настільки ж простим, як і додавання іншого рядка на кшталт yield return new TestCase(...). Це може йти у напрямку досліджувальних випробувань , що автоматизують створення тестових кейсів: "Давайте подивимося, що код повертається за всі секунди тижня тому".


0

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

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

Це все ще не означає, що у вашому коді немає помилок, а лише краще, ніж раніше, і ще раз все відоме поведінка є правильним!

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

Вимоги були відносно чіткими

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


0

Зробити логічну помилку дуже просто навіть у такому простому вихідному коді.

Так. Тестова розробка цього не змінює. Ви все одно можете створювати помилки у фактичному коді, а також у тестовому коді.

Тестова розробка не допомогла.

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

Також хвилює те, що я не бачу, як можна було б уникнути таких помилок.

Ви не можете. Навіть NASA не знайшла способу уникнути помилок; ми, звичайно, також не менші люди.

Окрім того, щоб більше писати код, перш ніж писати код,

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

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

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

Щодо критичного для місії коду, ви насправді могли б робити те, що ви сказали, але не для свого щоденного стандартного коду.

Як я міг у першу чергу уникнути створення цієї помилки?

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

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


-2

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

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

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

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

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

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


Це вкрай грізно. That in turn means you should spend less time using these techniques- але ви просто сказали, що це допоможе зменшити кількість помилок ?!
JᴀʏMᴇᴇ

@ JᴀʏMᴇᴇ більш прагматичне ставлення , який метод отримує вас максимальну віддачу для ваших buck.I знають людина , які пишаються тим , що вони витрачають в 10 разів писати тести , ніж вони зробили на свій код, і вони до сих пір є помилки Так бути розумними, а не догматичної, щодо методів тестування має важливе значення. І інтеграційні тести доцільно використовувати, тому докладайте більше зусиль, ніж для тестування одиниць.
gbjbaanb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.