Monday, February 1, 2016

Разработка через тестирование. Нужно ли это.

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

Нужно ли TDD?
Когда его стоит внедрять?
Замедляет ли работу написание тестов?
Какие подводные камни вы можете встретить?

Нужно ли TDD. 


Очень трудно ответить на этот вопрос, для начала нужно внести ясность. 

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

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

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

Или например вы пишите тесты, но высокого уровня. Например тесты на views для django. Или любые другие высокоуровневые или приемочные тесты  и только их. Это уже не TDD. Вы покрываете систему требованиями, но не контролируете архитектуру. Это разработка через приемочные тесты. 

Вот эта тонкая грань между псевдо-TDD и TDD сильно размыта и большая часть негатива или непонимания TDD идут именно из-за нее. 

Ну и финальный ответ на поставленный вопрос - прост. Обладая опытом разработки через TDD уже после пары проектов вы начнете видеть нужно ли TDD для каждой конкретной задачи. Поэтому как "идеальный программист" вы должны освоить эту методологию. 

Когда его стоит внедрять?



Непростой вопрос, прежде чем решить когда, нужно понять почему?

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

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

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

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

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

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

Во всех остальных случаях - вы партизан. Партизанить можно при code owning. В остальных случаях вы будете не эффективны.

Еще один из интересных способов внесения TDD в проект опять же связан с библиотеками. Например вы выбираете между библиотеками X, Y, Z. Напишите тесты на их использование. Т.е. не просто тестовые программы или скрипты, а полноценные unit-test. Обращайте внимание.

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

После того, как у вас есть готовые наборы тестов X, Y, Z вы можете оценить насколько удобно создавать объекты библиотеки, и насколько удобным будет использование библиотеки в вашем проекте.

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


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

Замедляет ли работу написание тестов?

Да. Как ни крути. А TDD замедляет работу еще больше. Потому что помимо кода вы пишите тесты. А потом проводите рефакторинг. Это время, время которое вы тратите.

Тогда зачем это нужно? Тесты дают определенную уверенность в надежности кода, в ваших изменениях. Это не означает что вы можете воткнуть if или переименовать метод, проверить что тесты работают и заливать в прод. Вы делаете ровно то, что и обычно, смотрите все места использования, диффы, анализируете код привычными средствами и дополнительно к этому прогоняете тесты.  Чем большее покрытие тестами, тем больше ваша уверенность в ваших изменениях. Но вероятность ошибки остается. Все зависит от того что именно вы протестировали, как протестировали, насколько осознанно подходите к вашим изменениях. Если я вас расстроил - не беда. Есть и хорошая новость. Из 7ми последних написанных мной микросервисов, написанных через TDD. 4 заработали после правильной конфигурации правильно и без ошибок. В трех нашлось незначительное количество ошибок, связанных с приведением типов данных, что я учел при дальнейшем написании тестов для подобных систем.

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

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

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

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


Финальное мое любимое преимущество. Вы теряете code owning, пишите тест, пишите функционал, делаете рефакторинг и все-равно кто писал затронутый код.  Следствием этого, является то, что ценность кода как такового в системе падает. Тесты главнее. Поэтому не стесняемся удалять неудачный код, не стесняемся писать сначала. Если ваше видение изменений сопротивляется тестированию, ломает все что было, возможно вы не на том пути. Остановитесь, откатитесь, подумайте, продолжайте. Когда я плотно подсел на TDD я по метрикам обнаружил что очень малая часть кода живет долго, появилось очень много удаленного кода. А сам код стал проще, потому что не таскает за собой артефакты.

Какие подводные камни вы можете встретить?

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

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

Следующее следствие. Если вы разрабатываете через TDD, то очень странно увидеть ситуацию когда разработчик выкатил кривой код с поломанными тестами ( например не запустил "долгоиграющий" набор тестов, а ограничился запуском лишь "быстрых" тестов. Поэтому вам нужна система постоянного запуска тестов и система оповещений. Что неразрывно связано с деплоем, что очень сильно сказывается на организации структуры кода и запуска, а также работы с зависимостями. Простой пример. Ваша команда пишет 100500 джанго проектов и для всех проектов есть virtualenv на все случаи жизни. При внедрении CI нужно строить virtualenv с нуля. Делать checkout, build проектов начисто. Это может оказаться непривычно и придется тратить время на изучение нового, возможно изменение системы конфигурации, запусков.

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

No comments:

 
Каталог сайтов, Добавить сайт