Я недавно закончил проект с помощью TDD, и я нашел, что процесс был определенным кошмаром. Я любил писать тесты сначала и наблюдать, что мой код растет, но как только требования начали изменяться, и я начал делать рефакторинги, я нашел, что провел больше времени, переписывая / фиксация модульных тестов, чем я сделал написание кода, намного больше времени на самом деле.
Я чувствовал, в то время как я проходил этот процесс, будет намного легче сделать тесты после того, как приложение было закончено, но если я сделал это, я был бы потерянных все преимущества TDD.
Так есть ли какие-либо хиты / подсказки для написания удобного в сопровождении кода TDD? Я в настоящее время читаю Roy Osherove Искусство Поблочного тестирования, есть ли какие-либо другие ресурсы, которые могли выручить меня?
Спасибо
Практика
Чтобы научиться писать достойные модульные тесты, требуется время. В сложном проекте (больше похожем на проекты) нет ничего странного.
Уже рекомендованная книга xUnit Test Patterns хороша, и я слышал хорошие отзывы о книге, которую вы сейчас читаете.
Что касается общих советов, это зависит от того, что было сложным в ваших тестах. Если они часто ломались, это могут быть не модульные тесты, а тем более интеграционные тесты. Если бы их было сложно настроить, SUT (Тестируемая система) могла бы показывать признаки того, что они слишком сложны, и потребовала бы дальнейшей модульности. Этот список можно продолжить.
Некоторые советы, по которым я живу, следуют правилу AAA .
Устраивайте, действуйте и утверждайте. Каждый тест должен следовать этой формуле. Это делает тест удобочитаемым и легким в обслуживании, если и когда они сломаются.
Дизайн по-прежнему важен
Я практикую TDD, но перед написанием кода я беру доску и что-то строчу. Хотя TDD позволяет вашему коду развиваться, некоторая предварительная разработка всегда является преимуществом. Тогда у вас, по крайней мере, есть отправная точка, отсюда ваш код может управляться написанными вами тестами.
Если я выполняю конкретную сложную задачу, я делаю прототип. Забудьте о TDD, забудьте о лучших практиках, просто выпустите немного кода. Очевидно, что это не производственный код, но он обеспечивает отправную точку. На основе этого прототипа я думаю о реальной системе и о том, какие тесты мне нужны.
Загляните в блог Google Testing - это был поворотный момент для меня, когда я начинал TDD. Статьи Misko (и сайт - особенно Guide to Testable ) превосходны и должны указать вам правильное направление.
Из здесь похоже, что отслеживание изменений - это таблица общего назначения в том же каталоге базы данных, что и таблицы. Поэтому при любом перемещении базы данных следует брать с собой таблицу изменений.
Сначала я изучу схему базы данных и воспользуюсь страницами MSDN.
-121--3702177- Если вы можете решить проблему с помощью ode15s
Matlab, вы должны иметь возможность решить ее с помощью vode
решателя scipy. Чтобы смоделировать ode15s
, я использую следующие настройки:
ode15s = scipy.integrate.ode(f)
ode15s.set_integrator('vode', method='bdf', order=15, nsteps=3000)
ode15s.set_initial_value(u0, t0)
, а затем вы можете с радостью решить проблему с помощью ode15s.integrate (t_final)
. Это должно работать довольно хорошо над жесткой проблемой.
(См. также http://www.scipy.org/NumPy_for_Matlab_Users )
-121--2258388-Да, существует целая книга под названием xUnit Test Patterns , в которой рассматривается эта проблема.
Это книга подписей Мартина Фаулера, поэтому в ней есть все следы классической книги узоров. Нравится вам это или нет - вопрос личного вкуса, но я, с одной стороны, счел его безмерно бесценным.
В любом случае суть вопроса заключается в том, что вы должны относиться к тестовому коду как к производственному коду. Прежде всего, следует придерживаться принципа DRY , поскольку это облегчает рефакторинг API.
Широко ли вы используете интерфейсы, Dependancy Injection и mocking?
Я считаю, что разработка интерфейсов и последующая инъекция реализаций этих интерфейсов с помощью DI-фреймворка, такого как ninject, значительно упрощает макетирование частей приложения, что позволяет правильно тестировать компоненты в изоляции.
Это облегчает внесение изменений в одну область без сильного влияния на другие, или, если изменения должны распространиться, вы можете просто обновить интерфейсы и работать над каждой отдельной областью по очереди.
Похоже, ваши модульные тесты хрупкие и пересекаются. В идеале одно изменение кода должно повлиять только на один модульный тест - при однозначном сопоставлении тестов с функциями другие тесты не зависят от данной функции. Это может быть слишком идеалистично; на практике многие из наших тестов повторно выполняют один и тот же код, но об этом следует помнить. Когда одно изменение кода влияет на множество тестов, это запах. Также, что касается вашего конкретного примера переименования: найдите инструмент, который автоматизирует эти рефакторинги за вас. Я считаю, что и Resharper, и CodeRush поддерживают такой автоматический рефакторинг; это гораздо более быстрый, простой и надежный способ рефакторинга, чем ручной подход.
Чтобы лучше изучить вашу IDE, ничто не сравнится с объединением в пару с кем-то другим. Вы оба узнаете; вы оба разовьете новые навыки - и это не займет много времени. Несколько часов значительно увеличат ваш комфорт с инструментом.
Я большой поклонник модульного тестирования, но у меня возникли проблемы с TDD (или базовым модульным тестированием) в моем последнем проекте. После проведения обзора после внедрения я обнаружил, что мы (я и остальная часть команды) столкнулись с двумя основными проблемами, связанными с нашей реализацией / пониманием TDD и модульного тестирования.
Первая проблема, с которой мы столкнулись, заключалась в том, что мы не всегда относились к нашим тестам как к гражданам первого класса. Я знаю, это звучит так, будто мы шли против философии TDD, но наши проблемы возникли после того, как мы выполнили большую часть первоначального дизайна и поспешили внести изменения на лету. К сожалению, из-за нехватки времени более поздняя часть проекта была заторможена, и мы попали в ловушку написания наших тестов после того, как был написан код. Поскольку рабочий код, установленный под давлением, был проверен в системе управления версиями без проверки того, прошли ли модульные тесты. По общему признанию, эта проблема не имеет ничего общего с TDD или модульным тестированием, а скорее является результатом сжатых сроков, среднего командного взаимодействия и плохого руководства (я буду винить здесь себя).
При более глубоком изучении неудачных модульных тестов мы обнаружили, что мы слишком много тестировали, особенно учитывая наши временные ограничения. Вместо использования TDD и сосредоточения нашего тестирования на коде с высокой отдачей мы использовали TDD и писали тесты для всей базы кода.Это сделало нашу долю модульных тестов в коде намного выше, чем мы могли поддерживать. Мы (в конце концов) решили использовать только TDD и написать тесты для бизнес-функциональности, которая, вероятно, изменится . Это уменьшило потребность в поддержке большого количества тестов, которые по большей части очень редко (или никогда) не менялись. Вместо этого наши усилия были лучше сфокусированы и направлены на более полный набор тестов для тех частей приложения, которые нам действительно интересны.
Надеюсь, что смогу извлечь уроки из моего опыта и продолжить разработку TDD или, по крайней мере, все еще разрабатывать модульные тесты для вашего кода. Лично я нашел следующие ссылки чрезвычайно полезными, помогая мне понять такие концепции, как выборочное модульное тестирование.
"Как только требования начали меняться и я начал делать рефакторинг, я обнаружил, что трачу больше времени на переписывание / исправление юнит-тестов"
И что? Почему это проблема?
Ваши требования изменились. Это означает, что ваш дизайн должен был измениться. Это значит, что ваши тесты должны были измениться.
"Я потратил больше времени на переписывание / исправление юнит-тестов, чем на написание кода, гораздо больше времени."
Это значит, что вы все делаете правильно. Требования, дизайн и влияние тестов были учтены при тестировании, и ваше приложение не потребовало больших изменений.
Так и должно быть.
Идите домой счастливым. Вы выполнили работу должным образом.
Я думаю, вы можете найти достойный баланс между тестированием и кодированием.
Когда я начинаю проект, поскольку требования и цели все время меняются, я почти не пишу какой-либо тест, потому что, как вы заметили, для постоянного исправления тестов потребуется много времени. Иногда я просто пишу в комментарии «это надо проверить», чтобы не забыть протестировать.
В какой-то момент вы чувствуете , что ваш проект обретает форму. Это момент, когда пригодится тяжелое модульное тестирование. Я пишу как можно больше.
Когда я начинаю много заниматься рефакторингом, я не особо беспокоюсь о тестах, пока проект снова не успокоится. Я также поместил несколько комментариев «проверьте это». Когда рефакторинг закончен, пора переписать все неудачные тесты (и, возможно, отбросить некоторые из них и, конечно, написать несколько новых).
Писать тесты таким способом - настоящее удовольствие, потому что это означает, что ваши проекты достигли определенного рубежа.
Прежде всего, рефакторинг не нарушает модульных тестов. Применяли ли вы рефакторинг по книге ? Возможно, ваши тесты проверяют реализацию, а не поведение, что может объяснить, почему они ломаются.
Модульное тестирование должно быть тестированием черного ящика , проверять, что делает модуль, а не как он это делает.
Вы используете хорошую IDE? Я задавал себе тот же вопрос, что и вы, несколько лет назад, когда впервые применил модульное тестирование. Тогда я использовал комбинацию Emacs
, find
и grep
для выполнения рефакторинга. Это было больно.
К счастью, коллега ударил меня по голове и убедил попробовать использовать «современные инструменты», что на его просторечии означало Intellij IDEA . IDEA - мое личное предпочтение, но Netbeans или Eclipse также справятся с основами. Трудно переоценить прирост производительности, который это мне дало; легко на порядок, особенно для больших проектов с большим количеством тестов.
Если у вас есть IDE, если вы все еще сталкиваетесь с проблемами, пора рассмотреть принцип DRY , который стремится обеспечить хранение информации только в одном место (константа, файл свойств и т. д.), чтобы, если вам понадобится изменить его позже, эффекты пульсации будут минимизированы.
Если ваши тесты трудно поддерживать, это признак того, что ваш код хрупкий. Это означает, что определение класса часто меняется.
Рассмотрите возможность определения обязанностей класса при обращении к внедренному интерфейсу. Думайте об этом больше с точки зрения передачи данных или отправки сообщения, а не управления состоянием.