Каковы недостатки написания кода перед написанием модульных тестов?

33

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

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

k25
источник
7
+1 за вопрос, почему определенная практика является «лучшей практикой», прежде чем принять ее
TaylorOtwell

Ответы:

37

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

Требования (тесты), разработанные после кода на основе кода, лишают вас такой уверенности, такой уверенности.

Карл Манастер
источник
+1 - отлично, точка и точка заняты! Спасибо за ваше мнение!
k25
7
Тесты! = Требования. Оба теста и код должны быть получены из требований.
Барт ван Инген Шенау
2
@ Барт ван Инген Шенау: Сила TDD как раз в том, что тестирует требования ARE. Более того, они являются исполняемыми требованиями.
Mouviciel
1
@Bart: модульные тесты часто слишком детализированы для (высокого уровня) требований заказчика, но идея определенно верна, особенно если мы также рассмотрим тесты более высокого уровня, такие как автоматические приемочные тесты, которые после их написания должны быть окончательными требованиями. В этом суть «гибкого приемочного тестирования».
Мартин Уикман,
3
TDD - это не тестирование, а спецификация. Тесты, основанные на подходе TDD, являются средством связи между разработчиком и заказчиком для согласования того, какой продукт должен быть сделан.
Mouviciel
18

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

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

Anon.
источник
Да, вы правы насчет первого пункта, но я всегда стараюсь не делать этого. Если тест не пройден, я всегда перехожу к коду и проверяю его правильность, а затем проверяю, верен ли мой тест, и затем изменяю, что не так. Спасибо за ваше мнение, я буду помнить об этом .. +1 от меня за 1-е и последнее очко ...
k25
2
Но что, если тест пройден? Тест может пройти, потому что он на самом деле не использует интересующий код; в TDD такого не может быть, потому что тест должен изначально провалиться, и если да, то тест не будет выполнен, пока вы не исправите его. Итак, в последнем тесте есть режим сбоя, которого нет в тестовом.
Карл Манастер
@Carl Manaster - Да, у вас есть правильная точка. После того, как я напишу код, я прекрасно осведомлен о требованиях, и поэтому мой пример модульного теста будет правильным (в идеале). Если мой тестовый пример пройден, я бы сказал, что код верен, если тест не пройден, я буду следовать тому, что сказал. Но я на 100% согласен с тем, что у вас есть верный смысл.
k25
@ k25: Дело в том, что если ваш тестовый пример пройден, вы все равно не знаете, правильный код или нет. Тестовый пример может быть неправильным.
Анон.
@Anon. - да, вы правы, я также приму это к сведению.
k25
12

На самом деле, люди зацикливаются на TDD о тестировании, хотя забывают о двух других буквах в аббревиатуре. Нечто, что можно прочитать здесь: TDD без T или TDD не о тестировании .

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

Чтобы даже иметь возможность писать модульные тесты «правильным способом», то есть, чтобы они были изолированными, быстрыми и автоматизированными, вы, вероятно, заметите, что требуется некоторое переосмысление того, как упорядочить ваш код так, чтобы ваш код стал проще тестировать.

Лично я изучил принципы SOLID, не зная, что такое написано. Это потому, что написание модульных тестов вынудило меня переписать классы, чтобы они не стали слишком сложными для тестирования. Это привело к таким вещам, как:

  • Мне пришлось перенести функциональность, которая либо не имела смысла, либо находилась в частных методах, на отдельные классы, чтобы я мог тестировать их отдельно. (Принцип единой ответственности).
  • Мне пришлось избегать больших структур наследования и расширять реализации с помощью композиции (что заметно в принципе Open-Closed).
  • Я должен был уметь наследовать, я использовал абстрактные классы всякий раз, когда видел общий код, которым можно делиться, и использовал методы-заглушки (принцип подстановки Лискова).
  • Мне пришлось писать интерфейсы и абстрактные классы, чтобы я мог тестировать классы в разделении. Что непреднамеренно заставляет вас писать фиктивные объекты. (Принцип разделения интерфейса)
  • Поскольку я написал много интерфейсов и абстрактных классов, я начал объявлять переменные и параметры для использования общего типа (принцип инверсии зависимости).

Несмотря на то, что я не делаю тестирование в первую очередь, я все же следую хорошим принципам и методикам ОО, которым вы начинаете следовать, просто чтобы сделать тестирование немного легче. Сейчас я не пишу код для себя. Я написал код, чтобы его можно было легко проверить или, что более важно; легко поддерживается .

Spoike
источник
1
+1 для SOLID естественно приходит вам в голову, когда вы думаете о разработке программного обеспечения.
ocodo
+1 (на самом деле я хотел дать +10, но не могу). Именно мои мысли - у вас список пунктов был чрезвычайно хорош. Это одна из причин, по которой я задал этот вопрос. Я почувствовал, что классов стало намного больше, когда я начал писать модульные тесты после того, как написал код. Но я хотел увидеть преимущества / недостатки обеих сторон, спасибо за ваше мнение!
k25
10

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

RationalGeek
источник
4

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

Например, вы можете подумать, что вам нужен метод, который принимает определенный набор параметров. И если вы сначала написали код, вы бы написали его таким образом, чтобы тест соответствовал заданным параметрам. Но если вы сначала напишете тест, вы можете подумать: «Подождите минутку, я бы не хотел использовать этот параметр в основном коде, поэтому, возможно, мне следует изменить API».

скоро
источник
+1 за первый балл. Но, не доходя до уровня параметров, что, если проект был обсужден с другими и принят?
k25
@ k25 - если что-то сложно использовать, как задумано, нужно больше думать. Иногда - очень редко - это просто сложная задача. Но чаще всего его можно свести к более простым задачам. У меня нет ссылки, но Гослинг или Гетц давали интервью о разработке API несколько лет назад ... стоит погуглить.
Anon
конечно, спасибо за указатели, я обязательно
загляну
2

Как я вижу много рекомендаций, чтобы начать писать тесты, а затем перейти к кодированию,

Для этого есть веская причина.

Если вы говорите «делайте то, что чувствуете правильно», люди совершают самые глупые и безумные поступки.

Если вы говорите «сначала пишите тесты», люди, по крайней мере, могут попытаться сделать правильные вещи.

Каковы недостатки, если я делаю это по-другому - написать код, а затем модульные тесты?

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

Однако это только «обычно». Некоторые люди разрабатывают проекты и тесты параллельно. Некоторые люди ставят тестируемый код на место и пишут тесты без переделки.

Правило «Сначала проверяй» специально предназначено для обучения и инструктирования людей, которые вообще ничего не знают.

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

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

Правила изложены очень строго по причине.

То, что вы делаете, это ваша собственная проблема.

С. Лотт
источник
2

смысл написания теста в первую очередь в том, что он заставляет задуматься

  • как проверить код
  • интерфейс, который должен присутствовать в коде, чтобы быть тестируемым

если вы делаете что-то простое, то, вероятно, не имеет значения, какой из них вы пишете первым (хотя полезно развивать привычку тестировать в первую очередь), поскольку тест будет простым и интерфейс будет очевидным

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

Стивен А. Лоу
источник
1

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

  1. Страховочная сеть тестов - позволяет вам делать большие изменения, не боясь что-то неосознанно сломать
  2. Органический дизайн - дизайн, которым я заканчиваю, обычно отличается от дизайна, который я сделал бы с нуля, и он всегда был лучше
  3. Продуктивность - работа для достижения маленьких целей (пройти этот тест) и выполнения этого (все тесты пройдены) работают очень хорошо для меня и поддерживают меня мотивированными. Добавьте в пару, и моя производительность достигнет новых максимумов.

Книги: Бек, К. Разработка, управляемая тестами, на примере

Хороший пример: http://jamesshore.com/Blog/Lets-Play/

Майк Полен
источник
+1 - хорошие очки (особенно 1-е) и спасибо за ссылки!
k25
0

Когда вы пишете тест, как вы узнаете, что он обнаружит состояние сбоя? Ответ - «проверь тест». Для этого сначала нужно написать тест, увидеть, как он провалился, и увидеть, как он прошел, только когда тестируемый модуль был успешно закодирован (цикл красный / зеленый / рефакторинг, упомянутый в одном из других ответов).

Сначала написание кода, а затем тестирование оставляют открытым вопрос о том, будет ли тест действительно провален.

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

Zenilogix
источник