TDD: я делаю это правильно?


14

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

Что я делаю:

  1. Подумайте о новом методе, который мне нужен.
  2. Создайте тест для этого метода.
  3. Неудачный тест.
  4. Напишите метод.
  5. Пройдите тест.
  6. Рефакторинг метод.
  7. Повторение.

Я делаю это для КАЖДОГО метода, который я пишу, есть ли некоторые, с которыми мне не стоит беспокоиться? Позже я обычно думаю о способе тестирования уже существующих методов другим способом или ситуацией. Должен ли я сделать эти новые тесты, о которых я думаю, или, поскольку у каждого метода уже есть свой тест, не стоит ли мне беспокоиться? Могу ли я НАБЛЮДАТЬ за тестирование своего кода? Думаю, это моя главная задача, чтобы спросить об этом

РЕДАКТИРОВАТЬ

Кроме того, это было то, что мне просто интересно. При выполнении чего-то вроде создания графического интерфейса, TDD будет необходимо в этой ситуации? Лично я не могу придумать, как написать для этого тесты.


5
Вы уже делаете это намного лучше, чем опытные профессионалы, которые говорят, что они все тестируют (но не делают).
Яннис

То, что вы описываете, не является духом TDD.

1
Вы можете посмотреть в ATDD или BDD.
dietbuddha

Возможно, начните выше - подумайте о новом модуле, который вам нужен.

Ответы:


16

То, что вы описываете как рабочий процесс, на мой взгляд, не дух TDD.

Синопсис книги Кента Бекса об Амазонке гласит:

Проще говоря, разработка через тестирование предназначена для устранения страха при разработке приложений.Хотя некоторый страх полезен (его часто считают совестью, которая говорит программистам «быть осторожными!»), Автор считает, что побочными продуктами страха являются опытные, сварливые и необщительные программисты, которые не в состоянии воспринимать конструктивную критику. Когда команды программистов покупают TDD, они сразу же видят положительные результаты. Они устраняют страх, связанный с их работой, и лучше подготовлены для решения стоящих перед ними сложных задач. TDD устраняет пробные черты, обучает программистов общаться и побуждает членов команды искать критику. Однако даже автор признает, что раздражительность должна решаться индивидуально! Короче говоря, предпосылка TDD заключается в том, что код должен постоянно тестироваться и подвергаться рефакторингу.

Практический TDD

Формальное автоматическое тестирование, особенно модульное тестирование, каждый метод каждого класса так же плох как антишаблон и ничего не тестирует. Есть баланс, который нужно иметь. Вы пишете юнит-тесты для каждого setXXX/getXXXметода, это тоже методы!

Кроме того, тесты могут помочь сэкономить время и деньги, но не забывайте, что они требуют времени и денег на разработку, и они являются кодом, поэтому они требуют времени и денег на сопровождение. Если они атрофируются из-за нехватки обслуживания, они становятся пассивом больше чем выгодой.

Как и все, как это, есть баланс, который не может быть определен кем-либо, кроме вас самих. Любая догма в любом случае, вероятно, более неправильная, чем правильная.

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

Вам будет очень трудно найти много профессиональных магазинов, которые работают таким же образом. Просто не имеет смысла тратить деньги на тестирование вещей, которые для практических целей никогда не изменятся после проведения простого теста на дым. Написание формальных автоматизированных модульных тестов для .getXXX/.setXXXметодов является ярким примером этого, полной траты времени.

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

- Эдсгер В. Джикстра . (Написано в 1988 году, так что сейчас оно приближается к 4,5 десятилетиям.)

Смотрите также этот ответ .


1
Это в значительной степени относится к тому, что меня беспокоило. Я чувствовал, что не должен тестировать каждый метод, как был, но не был уверен. Похоже, мне все еще нужно прочитать еще кое-что о TDD.
cgasser

@kevincline Большую часть времени setXXX/getXXXвообще не нужно :)
Чип

1
Когда вы запомните этот тривиальный getXXX и ошибетесь или введете ленивую загрузку в getXXX и ошибетесь, тогда вы поймете, что иногда вы действительно хотите протестировать свои геттеры.
Фрэнк Шиарар

13

Ты очень близко. Попробуйте думать немного иначе.

  1. Подумай о новом поведении, которое мне нужно.
  2. Создайте тест для этого поведения.
  3. Неудачный тест.
  4. Написать новый или расширить существующий метод.
  5. Пройдите тест.
  6. Код рефакторинга.
  7. Повторение.

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

Кроме того, помните три правила дяди Боба TDD .

  1. Вам не разрешается писать какой-либо производственный код, если только он не прошел неудачный модульный тест.
  2. Вам не разрешено писать больше модульных тестов, чем достаточно для провала; и ошибки компиляции - это ошибки.
  3. Вам не разрешено писать больше производственного кода, чем достаточно для прохождения одного неудачного модульного теста.

1
@Zexanima: У тебя дела идут лучше, чем у большинства из нас через год. Просто пытаюсь указать вам на следующий шаг.
ПДР

2
Я думаю, что эти 3 правила, на которые вы ссылаетесь; Какими бы идиллическими они ни казались, они исключительно догматичны и крайне нереалистичны в 99% всех производственных цехов, с которыми сталкивается каждый.

1
@FrankShearar или это может быть рассмотрено как непрактичная болтовня фундаменталистского экстремиста и не принимается во внимание. Я работал в магазинах с таким догматическим отношением, они буквально восприняли догму и упустили суть; написание тестов, которые не тестировали ни один из их реальных кодов практическим способом, и в итоге просто тестирование способности фреймворков Mocking и Dependency Injection смешивать то, что было в лучшем случае важно.

1
@pdr Дух чего-то диаметрально противоположен догматической формализованной канонизации этой вещи. Одно дело иметь философию, а другое - превращать ее в религию . TDD чаще всего обсуждается в черных и белых догматических религиозных терминах. Эти 3 правила звучат догматично и религиозно в представлении, и то, что слышно, - это Тест, Тест, Тестовая мантра, для кого-то вроде ОП, они воспринимают их буквально, и это приносит больше вреда, чем пользы. Я возразил Фрэнку, что поляризационные заявления могут принести больше вреда делу, чем пользы.

2
Моя точка зрения заключалась в том, что догматизм исходит из слепого принятия чего-то как Евангелия . Возьмите поляризационное утверждение, попробуйте, заставьте его покинуть вашу зону комфорта. Вы не можете оценить компромиссы, связанные с TDD, если вы не попробуете экстремальный подход «все или ничего», так как у вас не будет данных .
Фрэнк Шиарар

5

Несколько вещей, чтобы добавить к ответам других:

  1. Существует такая вещь, как чрезмерное тестирование. Вы хотите, чтобы ваши юнит-тесты перекрывались как можно меньше. Нет смысла иметь несколько тестов для проверки одинаковых условий в одном и том же фрагменте кода. С другой стороны, когда вы реорганизуете свой рабочий код и у вас есть много тестов, которые перекрывают этот раздел, вам придется вернуться и исправить все эти тесты. Принимая во внимание, что если они не перекрываются, то одно изменение в лучшем случае нарушит только один тест.

  2. Только потому, что вы подумали о лучшем способе написания теста, я не вернусь туда и не начну его переписывать. Это относится к людям, которые продолжают писать и переписывать один и тот же класс / функцию, потому что они пытаются сделать его идеальным. Это никогда не будет идеально, так что двигайтесь дальше. Когда вы найдете лучший метод, держите его в голове (или добавьте в комментарии к тесту). В следующий раз, когда вы окажетесь там, и вы сразу увидите выгоду от перехода на новый путь, это время для рефакторинга. В противном случае, если функция завершена, и вы перешли, и все работает, оставьте его работающим.

  3. TDD фокусируется на обеспечении немедленной ценности, а не просто на проверке каждой функции. Когда вы добавляете функциональность, начните с вопроса «что нужно клиенту». Затем определите интерфейс, чтобы дать клиенту то, что ему нужно. Затем выполните все, что нужно, чтобы пройти тест. TDD почти как тестирование сценариев сценариев использования (включая все «что-если»), а не просто кодирование открытых функций и тестирование каждого из них.

  4. Вы спрашивали о тестировании кода GUI. Посмотрите шаблоны «Скромный диалог» и «MVVM». Идея, лежащая в основе обоих, заключается в том, что вы создаете набор классов «модели представления», которые на самом деле не имеют специфичной для пользовательского интерфейса логики. Однако эти классы будут иметь всю бизнес-логику, которая обычно является частью вашего пользовательского интерфейса, и эти классы должны быть на 100% тестируемыми. Остаётся очень тонкая оболочка пользовательского интерфейса и, да, обычно эта оболочка остается без тестового покрытия, но в этот момент у нее почти не должно быть логики.

  5. Если у вас есть большая часть существующего кода, как предлагали немногие, не стоит начинать добавлять модульные тесты абсолютно везде. Это займет у вас вечность, и вы не получите выгоды от добавления модульных тестов в 80% классов, которые стабильны и не изменятся в ближайшем (или не очень) будущем. Однако, для новой работы, я считаю использование разработки TDD со ВСЕМ кодом чрезвычайно полезным. Когда вы закончите, вы получите не только комплект автоматизированных тестов, но и реальная разработка имеет огромные преимущества:

    • Принимая во внимание тестируемость, вы напишите код, который является менее связанным и более модульным
    • Рассматривая ваш публичный контракт прежде всего, вы получите общедоступные интерфейсы, которые намного чище
    • Когда вы пишете код, проверка новой функциональности занимает миллисекунды по сравнению с запуском всего приложения и попыткой принудительного выполнения по правильному пути. Моя команда все еще выпускает код обработки ошибок, который даже не был выполнен ОДИН РАЗ, только потому, что они не могли получить правильный набор условий, которые должны были произойти. Удивительно, сколько времени мы тратим, когда позже в QA такие условия случаются. И да, большая часть этого кода - это то, что кто-то считал бы «не областью для больших изменений в будущем, когда тестирование дыма будет сделано».

1

Есть некоторые методы, которые не тестируются, а именно эти тесты. Тем не менее, есть кое-что, что нужно сказать о некоторых тестах, добавляемых после написания исходного кода, таких как граничные условия и другие значения, так что может быть несколько тестов для одного метода.

Хотя вы можете перепроверять свой код, обычно это происходит, когда кто-то хочет проверить каждую возможную перестановку входных данных, что не совсем похоже на то, что вы делаете. Например, если у вас есть метод, который принимает символ, вы пишете тест для каждого возможного значения, которое можно ввести? Это было бы то место, где вы бы переоценили, ИМО.


Ах, хорошо. Это не то, что я делаю. Я просто в конечном итоге думаю о другой ситуации, когда я могу протестировать свои методы в дальнейшем после того, как я уже сделал их первоначальный тест. Я просто следил за тем, чтобы эти «лишние» тесты стоили того, чтобы сделать их, или если они закончились.
cgasser

Если вы работаете с достаточно небольшими приращениями, вы обычно можете быть уверены, что ваш тест действительно работает. Другими словами, провал теста (по правильной причине!) Сам по себе тестирует тест. Но этот уровень «достаточно уверенного» не будет таким же высоким, как у тестируемого кода.
Фрэнк Шиарар

1

Как правило, вы делаете это правильно.

Тесты кодовые. Так что, если вы можете улучшить тест, сделайте его рефакторинг. Если вы считаете, что тест можно улучшить, смените его. Не бойтесь заменить тест на более качественный.

Я рекомендую при тестировании вашего кода избегать указания того, как код должен делать то, что он делает. Тесты должны смотреть на результаты методов. Это поможет с рефакторингом. Некоторые методы не требуют явного тестирования (т. Е. Простые методы получения и установки), поскольку вы будете использовать их для проверки результатов других тестов.


Я тоже писал тесты для геттеров и сеттеров, так что спасибо за этот совет. Это спасет меня от ненужной работы.
cgasser

«Некоторые методы не нуждаются в явном тестировании (например, простые методы получения и установки)» - Вы никогда не копировали / вставляли методы получения и установки и забыли изменить имя поля за ним? Смысл простого кода в том, что он требует простых тестов - сколько времени вы реально экономите?
ПДР

Я не имею в виду, что метод не проверен. Это просто проверяется, подтверждая, что установлены другие методы, или во время фактической настройки теста. Если метод получения или установки не работает должным образом, тест не пройден, потому что свойства не были установлены правильно. Вы проверяете их бесплатно, неявно.
Шлейс

Тесты геттера и сеттера не занимают много времени, поэтому я, вероятно, продолжу их выполнять. Однако я никогда не копирую и не вставляю свой код, поэтому не сталкиваюсь с этой проблемой.
cgasser

0

Мое мнение о TDD заключается в том, что инструментальные средства создали мир разработчиков стиля «наведи и щелкни». То, что инструменты создают заглушку для каждого метода, не означает, что вы должны писать тесты для каждого метода. Некоторые люди «переименовывают» TDD в BDD (развитие, основанное на поведении), где тесты гораздо более детализированы и предназначены для проверки поведения класса, а не каждого из них.

Если вы разрабатываете свои тесты для тестирования класса как предназначенного для использования, тогда вы начинаете получать некоторые преимущества, особенно когда вы начинаете писать тесты, которые выполняют немного больше, чем каждый метод, особенно когда вы начинаете тестировать взаимодействие этих классов. методы. Я полагаю, вы могли бы думать об этом как о написании тестов для класса, а не как методов. В любом случае вы все равно должны написать «приемочные тесты», в которых используется комбинация методов, чтобы убедиться в отсутствии противоречий или конфликтов в том, как они используются вместе.

Не путайте TDD с тестированием - это не так. TDD разработан таким образом, что вы пишете код для выполнения ваших требований, а не для проверки методов. Это тонкий, но важный момент, который часто теряется людьми, которые слепо пишут тестовый код для каждого метода. Дело в том, что вы должны писать тесты, которые гарантируют, что ваш код выполняет то, что вы хотите, а не то, что написанный вами код работает так, как он должен.

Справа есть несколько хороших ссылок о BDD v TDD. Проверь их.


0

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

После того, как вы узнали, что такое TDD, вы можете решить, что некоторые вещи не стоит тестировать. Это тот же подход, которому вы должны следовать во всем, и японские боевые искусства называют это « шухари ». (Ссылка также объясняет, как можно пройти этапы обучения без учителя, что, как я подозреваю, является тем, как большинство людей должны учиться.)


0

Я считаю, что вы переоцениваете.

Я практикую TDD в течение многих лет, и по моему опыту, когда TDD выполняется эффективно, вы получаете два основных преимущества:

  • Обеспечить быструю обратную связь
  • Включить рефакторинг

Обеспечить быструю обратную связь

Особенно с динамическими языками, я могу выполнить соответствующие тесты менее чем за секунду. И у меня есть наблюдатели файловой системы, запускающие эти тесты автоматически при изменении исходного файла на диске. Таким образом, у меня практически нет времени ожидания для тестов, и я сразу же узнаю, что код, который я пишу, работал так, как ожидалось. Таким образом, TDD приводит к очень эффективному способу работы.

Включить рефакторинг

Если у вас есть хороший набор тестов, вы можете безопасно провести рефакторинг, поскольку вы получите новое понимание того, как должна создаваться система.

Хороший набор тестов позволяет вам перекладывать ответственность в своем коде и при этом быть уверенным в том, что код работает, как и ожидалось, после перемещения. И вы должны быть в состоянии сделать это с небольшими изменениями в тестовом коде.

Если вы пишете тесты для каждого метода в вашей системе, то есть вероятность, что вы не сможете легко реорганизовать свой код, каждый рефакторинг вашего кода потребует значительных изменений в тестовом коде. И можете ли вы быть уверены, что тестовый код все еще работает, как ожидалось? Или вы случайно ввели код в тестовый код, который, следовательно, приводит к ошибке в рабочем коде?

Однако если вы, как и предлагалось в ответе pdr , при написании тестов сосредоточитесь на поведении, а не на методах , у вас будут тесты, которые потребуют гораздо меньше изменений при рефакторинге системы.

Или, как говорит Ян Купер в этой презентации (я процитировал по памяти, поэтому, возможно, не совсем правильно):

Причиной написания нового теста должно быть добавление нового поведения, а не добавление нового класса.


-2

Вы должны проверить каждый публичный метод.

Подвох в том, что если ваши публичные методы очень малы, вы, вероятно, выставляете слишком много информации. Обычная практика выставления каждого свойства как getXXX()фактического нарушает инкапсуляцию.

Если ваши публичные методы на самом деле являются поведением класса, то вы должны их протестировать. Если нет, то они не являются хорошими публичными методами.

РЕДАКТИРОВАТЬ: pdr ответ гораздо более полный, чем мой.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.