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

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

Я позволил комментарию скользить по мере того, как разговор перешел в Agile Software Practices. В целом они были открыты для идеи опробовать новые методологии и улучшить их, пока я не упомянул разработку через тестирование. Единственным ответом на эту фразу был:

«Разработка через тестирование переоценена».

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

Позвольте мне начать с определения, что такое разработка через тестирование (TDD).

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

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

Этот процесс также известен как «Красный, Зеленый, Рефакторинг».

Вот базовый пример метода Python, предназначенного для вычисления переменной C в теореме Пифагора:

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

Когда я выступал за TDD, я спросил, почему это не практикуется. Вот 4 ответа, которые я слышал чаще всего.

  1. «У нас есть отдел контроля качества, написание тестов - их работа»
  2. «Настройка теста, возможно, с использованием заглушек и заглушек, может потребовать значительных дополнительных усилий»
  3. «Нет никакой пользы»
  4. "Это медленно"

Поговорим об этом.

«У нас есть отдел контроля качества, их работа - написание тестов»

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

Я твердо верю, что разработчики несут ответственность за то, чтобы код, который они пишут, был:

  • Технически правильный, исходя из потребностей бизнеса
  • Легко понять
  • Проверяемый
  • Расширяемый
  • и просто

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

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

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

Однако я тоже был на другой стороне.

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

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

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

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

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

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

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

  • Это модульный тест или я позволил области немного расшириться, чтобы быть сервисным тестом?
  • Хорошо ли настроена моя структура кода, так что каждый класс и метод имеют только одну ответственность (иначе говоря, мой код не связан)?

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

«Нет никакой пользы»

По иронии судьбы, это возражение против использования TDD в основном исходит от разработчиков, которые даже не пробовали его. Даже Coldplay знал, что «никогда не узнаешь, если не попробуешь».

У TDD есть два основных преимущества.

Первый относительно ясен. Когда вы всегда пишете тест для всего функционального кода, прежде чем писать указанный код, у вас, по определению, есть Код самотестирования. Мартин Фаулер говорит:

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

Другими словами, ваш код делает именно то, что вы хотите, как определено вашими тестами.

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

Наличие этого самотестированного кода дает командам возможность действительно извлечь выгоду из непрерывной интеграции и развертывания.

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

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

«Это медленно»

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

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

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

* видит сравниваемые графики выгорания команд, закатывает глаза *

Когда я смотрю на четыре ключевых показателя (я часто на них ссылаюсь) из Accelerate, два из них - это частота сбоев при изменении и частота развертывания.

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

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

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

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

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

«Если болит, делайте это чаще и снимайте боль»

А теперь займитесь разработкой, основанной на тестировании, пока она не перестанет болеть :) Спасибо за чтение!

Другие статьи из моей серии о современных практиках разработки программного обеспечения можно найти здесь

РЕСУРСЫ: