Подтвердить что ты не робот

TDD - проблемы начинающих и камни преткновения

Пока я писал модульные тесты для большей части кода, который я сделал, я только недавно получил копию TDD на примере Кента Бек. Я всегда сожалел о некоторых дизайнерских решениях, которые я принял, поскольку они не позволяли приложению быть "проверяемым". Я прочитал книгу, и, хотя некоторые из них выглядят чужими, я чувствовал, что смогу это сделать, и решил попробовать ее в текущем проекте, который в основном представляет собой клиент-серверную систему, через которую эти две части общаются. USB. Один на гаджете, а другой на хосте. Приложение находится в Python.

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

Основываясь на моем опыте, у меня есть несколько вопросов, которые я хотел бы задать. Я получил некоторую информацию от Новый для TDD: есть ли примеры приложений с тестами, чтобы показать, как делать TDD?, но у вас есть некоторые конкретные вопросы, на которые я бы хотел ответить/обсудить на.

  • Кент Бек использует список, который он добавляет и удаляет, чтобы руководить процессом разработки. Как вы делаете такой список? Вначале у меня было несколько таких элементов, как "сервер должен запускаться", "сервер должен прервать, если канал недоступен" и т.д., Но они были смешанными, и, наконец, теперь это просто "клиент должен иметь возможность подключиться к серверу" (который запуск подключенного сервера и т.д.).
  • Как вы обрабатываете перезаписи? Сначала я выбрал полудуплексную систему на основе именованных каналов, чтобы я мог разработать логику приложения на своей собственной машине, а затем добавить часть связи USB. Они переместились, чтобы стать основанной на сокетах, а затем перешли от использования сырых сокетов к использованию модуля SocketServer Python. Каждый раз, когда что-то менялось, я обнаружил, что мне пришлось переписать значительную часть тестов, которые раздражали. Я подумал, что тесты будут несколько неизменным руководством во время моего развития. Они просто чувствовали, что больше кода обрабатывать.
  • Мне нужен клиент и сервер для общения по каналу для тестирования с каждой стороны. Я мог высмеять одну из сторон, чтобы проверить другую, но тогда весь канал не будет проверен, и я волнуюсь, что я пропущу это. Это уменьшило весь красный/зеленый/рефакторический ритм. Это просто недостаток опыта или я делаю что-то неправильно?
  • "Подделка, пока вы не сделаете это" оставила мне много грязного кода, который я позже потратил много времени на реорганизацию и очистку. Так ли это работает?
  • В конце сеанса теперь у меня есть клиент и сервер, работающий с 3 или 4 модульными тестами. Мне потребовалось около недели, чтобы это сделать. Думаю, я мог бы сделать это за один день, если бы использовал модульные тесты после кода. Я не вижу выигрыша.

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

P.S.: Пожалуйста, дайте мне знать, если это должна быть вики сообщества, и я помечаю ее так.

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

Обновление 1: Практика Практики Практики!

4b9b3361

Ответ 1

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

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

"Список" может быть довольно неформальным (это случай в книге Бэка), но когда вы переходите на создание элементов в тесте, попробуйте написать инструкции в "[Когда что-то случится с этим], тогда [это условие должно быть правдой в этом] формате. Это заставит вас больше думать о том, что именно вы проверяете, как вы его проверите и переводите непосредственно в тесты - или, если это не так, вы должны дать понять, какая часть функций отсутствует. Представьте пример использования/сценарий. Например," сервер должен запускаться" неясно, потому что никто не инициирует действие.

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

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

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

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

"Подделка, пока вы не сделаете это" оставил меня с большим количеством грязного кода, который я позже потратили много времени на рефакторинг и очистить. Так ли это работает?

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

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

Опять же, это требует практики, и вы должны со временем ускоряться. Кроме того, иногда TDD более плодотворен, чем другие, я обнаружил, что в некоторых ситуациях, когда я точно знаю код, который я хочу написать, он просто быстрее пишет хорошую часть кода, а затем записывает тесты.
Помимо Бэка, одна книга, которую мне нравилась, - "Искусство модульного тестирования" Роя Ошерове. Это не TDD-книга, а она .Net-ориентированная, но вы можете взглянуть на нее в любом случае: хорошая часть состоит в том, как писать ремонтные тесты, качество тестов и связанные с ними вопросы. Я обнаружил, что книга резонировала с моим опытом после написания тестов и иногда изо всех сил пыталась сделать это правильно...
Поэтому мой совет: не бросайте полотенце слишком быстро и дайте ему немного времени. Вы также можете захотеть сделать что-то проще - тестирование связанных с сервером вещей не похоже на самый простой проект, чтобы начать с него.

Ответ 2

  • Кент Бек использует список... наконец, теперь это просто "клиент должен иметь возможность подключиться к серверу" (который включал запуск сервера и т.д.).

Часто плохая практика.

Раздельные тесты для каждого отдельного уровня архитектуры хороши.

Консолидированные тесты, как правило, скрывают архитектурные проблемы.

Однако проверяйте только публичные функции. Не каждая функция.

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

2. Как вы обрабатываете перезаписи?... Я обнаружил, что мне пришлось переписать значительную часть тестов.

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

и

Да, значительное изменение архитектуры означает значительное изменение теста.

и

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

3. Мне нужен клиент и сервер для общения по каналу для проверки с обеих сторон. Я мог высмеять одну из сторон, чтобы проверить другую, но тогда весь канал не будет проверен...

Есть модульные тесты. С издевательствами.

Существуют интеграционные тесты, которые проверяют все это.

Не путайте их.

Вы можете использовать инструменты unit test для проведения тестов интеграции, но они разные.

И вам нужно сделать оба.

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

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

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

5. Я не вижу выигрыша.

Все истинные гении обнаруживают, что тестирование замедляет их.

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

Если вам не нужен proof, который работает с вашим кодом, вам не нужно тестировать.

Ответ 3

Q. Кент Бек использует список, к которому он добавляет и удаляет его, чтобы руководить процессом разработки. Как вы делаете такой список? Вначале у меня было несколько таких элементов, как "сервер должен запускаться", "сервер должен прервать, если канал недоступен" и т.д., Но они были смешанными, и, наконец, теперь это просто "клиент должен иметь возможность подключиться к серверу" (который запуск подключенного сервера и т.д.).

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

Server starts

Теперь я ищу любой более простой тест, который я могу написать. Что-то с меньшим изменением и меньшим количеством движущихся частей. Например, я могу считать "настроенный сервер правильно".

Configured server correctly
Server starts

Действительно, хотя "запуск сервера" зависит от "настроенного сервера правильно", поэтому я делаю эту ссылку понятной.

Configured server correctly
Server starts if configured correctly

Теперь я ищу варианты. Я спрашиваю: "Что может пойти не так?" Я могу настроить сервер неправильно. Сколько разных способов имеет значение? Каждый из них проводит тест. Как сервер не запустится, хотя я правильно его настроил? Каждый случай этого делает тест.

Q. Как вы обрабатываете перезаписи? Сначала я выбрал полудуплексную систему на основе именованных каналов, чтобы я мог разработать логику приложения на своей собственной машине, а затем добавить часть связи USB. Они переместились, чтобы стать основанной на сокетах, а затем перешли от использования сырых сокетов к использованию модуля SocketServer Python. Каждый раз, когда что-то менялось, я обнаружил, что мне пришлось переписать значительную часть тестов, которые раздражали. Я подумал, что тесты будут несколько неизменным руководством во время моего развития. Они просто чувствовали, что больше кода обрабатывается.

Когда я меняю поведение, я считаю разумным менять тесты и даже сначала их менять! Если мне нужно изменить тесты, которые напрямую не проверяют поведение, которое я в процессе изменения, это признак того, что мои тесты зависят от слишком большого числа разных поведений. Это интеграционные тесты, которые я считаю мошенничеством. (Google "Тесты интеграции - это мошенничество" )

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

Если я создаю клиент, сервер и канал, то я пытаюсь проверить каждый отдельно. Я начинаю с клиента, и когда я тестирую его, я решаю, как должен себя вести сервер и канал. Затем я реализую канал и сервер, чтобы соответствовать поведению, которое мне нужно. При проверке клиента я блокирую канал; при проверке сервера я издеваюсь над каналом; при проверке канала я блокирую и имитирую как клиент, так и сервер. Надеюсь, это имеет смысл для вас, так как я должен сделать некоторые серьезные предположения о природе этого клиента, сервера и канала.

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

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

Q. В конце сеанса у меня теперь есть мой клиент и сервер, на которых работают около 3 или 4 модульных тестов. Мне потребовалось около недели, чтобы это сделать. Думаю, я мог бы сделать это за один день, если бы использовал модульные тесты после кода. Я не вижу выигрыша.

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

Кроме того, не измеряйте кривую обучения. Мой первый реальный опыт TDD состоял в том, чтобы переписать 3-месячную работу в 9, 14-часовой рабочий день. У меня было 125 тестов, которые заняли 12 минут. Я понятия не имел, что я делаю, и это было медленным, но оно было устойчивым, и результаты были фантастическими. Я по существу переписал через 3 недели, что изначально потребовалось 3 месяца, чтобы ошибиться. Если бы я написал это сейчас, я мог бы сделать это через 3-5 дней. Разница? В моем тестовом наборе будет 500 тестов, которые будут выполняться в течение 1-2 секунд. Это пришло с практикой.

Ответ 4

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

Для новичков это на самом деле не так. Сначала идет дизайн. (Интерфейсы, объекты и классы, методы, whatevers, соответствующие вашему языку.) Затем вы пишите свои тесты на это. Затем вы пишете код, который действительно делает материал.

Прошло некоторое время с тех пор, как я посмотрел на книгу, но Бек, похоже, написал, как будто дизайн кода просто происходит бессознательно в вашей голове. Для опытных программистов это может быть правдой, но для noobs, подобных мне, nuh-uh.

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

Кодирование сложно. Пойдем по магазинам.

Ответ 5

Для первой точки, см. question Я спросил некоторое время назад, касаясь вашей первой точки.

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

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

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

Я рекомендую Google Testing Blog довольно сильно, потому что некоторые из статей там сделали мое тестирование для проектов TDD намного лучше.

Ответ 6

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

Я начал делать TDD, возможно, 6 месяцев назад? Я все еще учусь. Я могу сказать, что с течением времени мои тесты и код стали намного лучше, поэтому продолжайте. Я действительно рекомендую книгу XUnit Design Patterns.

Ответ 7

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

Элементы в списках TDD TODO более мелкие, чем те, которые предназначены для тестирования одного поведения только одного метода, например:

  • проверить успешное соединение с клиентом
  • проверить тип соединения с клиентом 1
  • Ошибка проверки соединения с клиентом типа 2
  • проверить успешную связь с клиентом
  • проверка связи с клиентом завершается неудачей, если не подключен

Вы можете создать список тестов (положительный и отрицательный) для каждого приведенного вами примера. Кроме того, при модульном тестировании вы не устанавливаете никакого соединения между сервером и клиентом. Вы просто вызываете методы в изоляции,... Это отвечает на вопрос 3.

Как вы обрабатываете перезаписи?

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

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