Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

2006 г.

Модульное тестирование и Test-Driven Development, или Как управлять страхом в программировании

Сергей Белов,
менеджер проекта компании StarSoft Development Labs

"IT News", #21/2005


Модульное тестирование имеет довольно длинную по компьютерным меркам историю. Впервые о нем заговорили в 1975 году (именно тогда оно упоминается в знаменитом "Мифическом человеко-месяце" Брукса), затем в 1979-м его подробно описал в книге "The Art of Software Testing" Гленфорд Майерс. А через 12 лет, в 1987-м, IEEE приняла специальный стандарт модульного тестирования ПО.

Тем не менее наблюдаемый в последние годы рост популярности модульных тестов связан почти исключительно с распространением так называемых "легких" методологий, и особенно с экстремальным программированием. Начало этой тенденции положил Кент Бек своей книгой "Extreme Programming Explained", увидевшей свет в 1999 году, где, помимо прочего, были сформулированы основные идеи Test-Driven Development (TDD). Главная мысль автора очень проста: если тестирование - это хорошо, значит, программисты должны постоянно тестировать свой код. Набор рекомендаций, позволяющих добиться этой цели на практике, и составляет сегодня ядро TDD. Существует несколько распространенных определений TDD. Каждое из них акцентирует внимание на определенной стороне вопроса. В первом приближении удобно считать, что TDD - это методика разработки, позволяющая оптимизировать использование модульных тестов. Хочется подчеркнуть, что речь идет именно об оптимальном, а не максимальном применении. Задача, которую преследует TDD, - достижение баланса между усилиями и результатом.

Три цвета

С практической точки зрения, основой TDD является цикл "red/green/refactor". В первой фазе программист пишет тест, во второй - код, необходимый для того, чтобы тест работал, в третьей, при необходимости, производится рефакторинг. Последовательность фаз очень важна. В соответствии с принципом "Test First", следует писать только такой код, который абсолютно необходим, чтобы тесты выполнялись успешно.

1.gif

Попробуем проиллюстрировать этот цикл простейшим примером. Допустим, нам необходим метод, преобразующий числа от 1 до 7 в названия соответствующих дней недели. Следуя принципу "Test First", вначале пишем тест для этого метода:

[Test, ExpectedException(typeof(ArgumentException))]
public void TestGetDayOfWeekNameInvalidArgument() {
  Converter.GetDayOfWeekName(8);
}

Затем, создаем метод-заглушку на тестируемом классе, необходимую для того, чтобы проект собрался:

static public string GetDayOfWeekName(int dayNumber) {
  return string.Empty;
}

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

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

static public string GetDayOfWeekName(int dayNumber) {
  throw new ArgumentException();
}

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

После завершения цикла "red - green - refactor" его нужно повторить для следующего участка функциональности. Для нас это случай, когда числа попадают в нужный диапазон. Пишем несложный тест и проверяем, что он не работает. Assert в модульных тестах является стандартным способом документировать наши предположения.

[Test]
public void TestGetDayOfWeekName() {
  Assert.AreEqual("Monday", Converter.GetDayOfWeekName(1));
  ...
  Assert.AreEqual("Saturday", Converter.GetDayOfWeekName(7));
}

В "зеленой" фазе мы снова заставляем тест работать.

static public string GetDayOfWeekName(int dayNumber) {
  switch (dayNumber) {
    case 1: return "Monday";
    ...
    case 7: return "Saturday";
  }
  throw new ArgumentException();
}

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

static public string GetDayOfWeekName(int dayNumber) {
  Hashtable ht = new Hashtable();
  ht[1] = "Monday";
  ht[7] = "Saturday";
  string result = ht[dayNumber] as string;
  if (result == null)
    throw new ArgumentException();
  return result;
}

Как правило, цикл "red - green - refactor" должен занимать от 5 до 20 минут, хотя исключения, конечно, встречаются. Не следует работать в нескольких фазах одновременно: рефакторить код, например, в "красной" фазе неразумно. Чем больше тестов отлаживается в одном цикле, тем хуже; в идеале нужно работать только с одним тестом одновременно.

Обычно программистам, впервые сталкивающимся с "Test First", этот стиль кажется значительно более трудоемким. Доля правды в этом, безусловно, есть: для тестирования все-таки приходится писать дополнительный код. Давайте попробуем разобраться, что мы приобретаем взамен.

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

  1. Традиционная: уменьшение количества "багов".
  2. Поддержка низкоуровневого дизайна.
  3. Поддержка рефакторинга.
  4. Поддержка отладки.
  5. Наконец, тест помогает документировать код.

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

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

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

Значение тестов для рефакторинга переоценить невозможно. Любой, даже самый небольшой рефакторинг, как известно, требует наличия написанных тестов. Эта мысль важна настолько, что Мартин Фаулер в своей книге "Refactoring" посвятил модульному тестированию целую главу. Конечно, некоторые виды рефакторингов, такие, как, например, переименование полей, вполне возможно применять и без тестов. Но набор модульных тестов, покрывающих большую часть приложения, позволяет модифицировать систему значительно более агрессивно и со значительно более предсказуемыми результатами. Именно сочетание рефакторинга и тестов позволяет XP-программистам быстро изменять систему в любом нужном заказчику направлении. И именно поэтому мы можем позволить себе не искать гибких решений, а использовать самые простые (цена изменений при наличии тестов не слишком высока).

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

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

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

Пять причин

В классическом XP тесты принято считать основным средством документирования кода, своего рода исполняемой спецификацией. Даже в том случае, если эта точка зрения кажется чересчур экстремальной, тесты можно применять по меньшей мере в качестве примеров использования кода. В отличие от настоящей документации тесты не могут не быть актуальными. Закономерный вопрос: если модульное тестирование в рамках TDD позволяет добиваться таких замечательных результатов, почему оно не было широко распространено до этого?

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

Начнем с самого простого аргумента типа "тестировать - не моя работа" или "тестеры и так все найдут". Он, как правило, выдвигается людьми, полагающими, что единственная задача модульного тестирования - снижение количества "багов". Возражения подобного рода обычно снимаются, когда удается объяснить, что TDD прежде всего помогает программистам, а не тестерам. Как только возражающая сторона осознает возможности модульного тестирования как инструмента разработки, такой аргумент, что называется, теряет силу.

Во-вторых, среди программистов бытует мнение, что тестировать - скучно. В рамках традиционного подхода, когда тесты пишутся после кода, для уже работающих модулей, тестировать действительно неинтересно. Нет ощущения, что создаешь что-то новое и полезное. Но в случае с "Test First" ситуация изменяется в противоположную сторону, и написание тестов превращается в дизайн методов, а дизайн - одна из самых увлекательных практик программирования. Кроме того, тесты дают возможность сразу ощутить положительный результат своего труда: цель находится не за горизонтом, а рядом и достижима очень быстро. Программисты, постоянно практикующие "Test First", на скуку не жалуются.

Не менее часто приходится слышать что-нибудь вроде "модульное тестирование - это здорово, но времени на него у нас нет". Что ж, как уже говорилось, тестирование действительно увеличивает затраты времени на кодирование (по разным оценкам, на величину от 15 до 100%). С другой стороны, тестирование радикально сокращает затраты времени на:

  • отладку;
  • рефакторинг;
  • общение между программистами и тестерами по поводу "багов" и другие последствия попадания ошибок в очередной build.

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

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

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

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

Практические рекомендации

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

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

  • коэффициент покрытия кода (code coverage);
  • количество тестов;
  • количество asserts;
  • количество строк кода в модульных тестах;
  • суммарное время исполнения тестов.

Наиболее важный показатель - коэффициент тестового покрытия, или code coverage, измеряемый как отношение числа инструкций, выполненных тестами, к общему числу инструкций в модуле или приложении. Общий coverage приложения является основным средством оценки полноты модульного тестирования, и в нашем случае даже существует соглашение с заказчиком относительно его минимально допустимого уровня. Как правило, удовлетворительным считается coverage не ниже 75% или более, в зависимости от конкретного приложения. 100% сoverage не является чем-то из ряда вон выходящим и достаточно легко достигается при использовании "Test First". Использовать сoverage для оценки состояния модульных тестов следует осторожно. Эта метрика скорее позволяет выявить проблемы, чем указать на их отсутствие. "Плохие" значения coverage четко сигнализируют о том, что тестов в приложении недостаточно, в то время как "хорошие" значения не позволяют сделать обратного вывода. Проблема в том, что полнота тестов никак не связана с их корректностью. За рамками coverage остается также важный вопрос о диапазонах параметров функций. Тем не менее coverage удобно применять, с одной стороны, для общего наблюдения за тестированием в проекте, а с другой - для выявления не покрытых тестами участков кода.

Три других показателя обычно имеет смысл рассматривать вместе. В отличие от coverage, количество тестов, asserts и строк кода интереснее всего наблюдать в динамике. При нормальном использовании TDD все три значения должны расти ежедневно и равномерно, причины резких изменений необходимо выявлять.

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

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

В заключение разговора о метриках отмечу, что в XP-группе хорошо зарекомендовала себя практика их ежедневного автоматизированного сбора с рассылкой report'а команде. Анализом результатов, как правило, занимаются "PM" и "техлид". Метрики, несмотря на удобство работы с ними, в большинстве случаев не позволяют оценить тесты по целому ряду важных неформальных критериев. Поэтому существует набор требований к тестам, отслеживаемых, как правило, на code review. К ним относятся:

  • Простота. Тесты кроме всего остального должны объяснять код, который они используют, и делать это максимально прозрачно для постороннего человека образом.
  • Правильное именование тестов. Существуют разные соглашения, но в любом случае название должно адекватно отражать суть теста.
  • Не допускается зависимость тестов друг от друга или от порядка вызова. Выполнение этого правила позволяет отлаживать тесты произвольными группами.
  • Тесты должны быть атомарными, каждый из них должен проверять ровно один тестовый случай. Громоздкие и сложные тесты необходимо разбивать на несколько более мелких.

Разумеется, к тестам применяются те же требования стандартов кодирования, что и к основному коду. Один из главных вопросов в модульном тестировании - что нужно тестировать и в каком объеме? Классический ответ TDD: тестировать нужно все, что потенциально может не работать. К сожалению, руководствоваться такой расплывчатой формулировкой в реальном проекте бывает не всегда просто. Наиболее практичный критерий - тестовое покрытие. Тестов должно быть написано как минимум столько, чтобы coverage находился в пределах нормы.

В соответствии с идеями TDD в большинстве случаев, кроме тестов, необходимых для обеспечения coverage, программисты пишут дополнительные тесты на ситуации, которые они по каким-либо причинам хотят проверить дополнительно. Такая практика приветствуется. Модульное тестирование, как уже отмечалось, не является "серебряной пулей" и может использоваться далеко не всегда. Так, например:

  • Обычно в модульных тестах не проверяется performance. С технической точки зрения, это можно делать, и некоторые тестировочные среды даже предоставляют для этого специальные средства. Но требования по производительности обычно указываются для приложения в целом, а не для отдельных функций; кроме того, performance testing часто занимает большое количество времени.
  • Как правило, не имеет смысла тестировать чужой код и автоматически сгенерированный код.
  • На уровне модульного тестирования часто тяжело или невозможно проверять сложные функциональные требования к приложению. Этим у нас занимаются тестеры с помощью автоматизации или ручного тестирования.

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

Необходимость запускать тесты отдельными наборами заставляет использовать механизмы структурирования тестов. В зависимости от конкретной среды, тесты организуют либо в иерархические наборы (Boost Test Library), либо в пространства имен (NUnit). Многие frameworks, кроме того, предоставляют возможность задать категорию теста, это удобно для того, чтобы запускать тесты из разных веток одновременно. Правила категоризации тестов имеет смысл определять в стандартах кодирования.

Важнейшим паттерном модульного тестирования, поддерживаемым всеми развитыми frameworks, являются SetUp/TearDown-методы, предоставляющие возможность выполнения кода перед и после запуска теста или набора тестов. Как правило, существуют отдельные методы SetUp/TearDown уровня тестов и test suites. Важной и очень удобной возможностью являются SetUp/TearDown-методы для всех тестов (уровень сборки в терминах mbUnit).

Основная задача SetUp/TearDown - как правило, создание тестовых наборов данных. При тестировании кода, работающего с базами данных на запись, в этих методах производится backup и восстановление базы либо создаются и откатываются транзакции.

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

Mock-объекты удобно использовать, если:

  • заменяемый объект не обладает необходимым быстродействием;
  • заменяемый объект тяжело настраивать;
  • нужное поведение заменяемого объекта сложно смоделировать;
  • для проверки call-back-функций;
  • для тестирования GUI.

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

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

[RowTest]
[Row("Monday", 1)]
...
[Row("Saturday", 7)]
public void TestGetDayOfWeekName(string result, int arg) {
  Assert.AreEqual(result, Converter.GetDayOfWeekName(arg));
}

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

[Factory]
public static int[] Numbers() {
  int[] result = { 1, ..., 9 };
  return result;
}

[CombinatorialTest ]
public void TestMultiplicationTable (
  [UsingFactories("Numbers") int lhs,
  [UsingFactories("Numbers") int rhs) {
  Assert.AreEqual(lhs * rhs, Foo.Multiply(lhs, rhs));
}

Прямая поддержка комбинаторного и строчного тестирования во framework серьезно облегчает тестирование чувствительного к параметрам кода. Хорошим примером такого framework является mbUnit.

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

Главный инструмент модульного тестирования, конечно, unit test framework. Большинство современных framework базируются на дизайне, предложенном Беком в 1994 году в статье "Simple Smalltalk Testing". Задача framework - предоставлять библиотеки для создания тестов и средства их запуска. При выборе framework, с технической точки зрения, наиболее важно учитывать наличие необходимых клиентов (командная строка, GUI, модули для запуска из-под NAnt/Ant или IDE), поддержку используемых паттернов тестирования и reporting.

Поддержка тестирования из IDE в идеале должна включать в себя средства для запуска тестов по одному и группами с разной гранулярностью, под отладчиком и без него. Полезной является возможность измерения coverage для классов и сборок. Для применения "Test First" удобны средства генерации пустых определений для еще ненаписанных методов.

Анализаторы и метрики

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

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

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

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

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

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

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

В этом случае тесты пишутся только тогда, когда затрагивается какой-либо код: во-первых, при написании нового кода; во-вторых, при рефакторинге; в-третьих, во время bug-fixing. Неплохо зарекомендовал себя подход, при котором весь новый код должен иметь coverage значительно больший, чем общепроектная норма, вплоть до 100%.

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

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...