Статьи

Как серьезно сдать юнит-тестирование (и тест-драйв)

Поэтому мы пишем некоторый тестовый код, затем пропускаем его и перезапускаем. Если у нас еще есть несколько минут в текущем Pomodoro, давайте немного проведем рефакторинг и извлечем некоторые методы. Легко ли?

Ну, у Test-Driven Development есть простое определение, но много последствий и предположений, особенно в части рефакторинга. Вот несколько советов из моего опыта нескольких лет TDD в PHP и Java, в веб-приложениях и приложениях машинного обучения.

Просто хорошее письмо

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

public void increasesItsHeightWhenANewRowIsAdded()

более читабельно, чем:

public void processingNewRow()

или даже

public void testAddMethod()

При считывании имен тестовых методов поведение класса должно быть вам понятно:

public void changesItsHeightWhenANewRowIsAdded()
public void reducesItsHeightWhenANewRowIsAdded()
public void anEmptyRowCantBeAdded()

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

Случаи ошибок

Проблема с наивным TDD заключается в тестировании проекта API (имен методов и аргументов), но не в случаях ошибок. Действительно тщательное тестирование основано не только на удачных путях, но и на управлении ошибками; ошибки, которые будут происходить особенно при работе с внешними объектами.

Таким образом, интерфейс, которым вы управляете, также должен формироваться из-за ошибок:

  • возвращаемые значения . Нет возвращаемого значения, очевидно, тривиально; в противном случае, это последовательный тип? Вы возвращаете false в некоторых случаях? Можете ли вы вернуть нулевой объект? Может быть интерфейс для определения результата, который воспринимает хорошие и плохие результаты, например тип Maybe.
  • побочные эффекты и уведомления . Первый вид предназначен, обязательные эффекты звонков, которые вы делаете. Второе охватывает логирование, события и подписчики (как представления). Попытайтесь уменьшить эфферентную связь (прямо говоря, количество объектов, которые вы имеете как соавторы).
  • исключения являются частью API; аннотация @throws и предложения throws моделируют их. Некоторые тесты должны быть выделены, чтобы попытаться вызвать эти исключения и убедиться, что они на самом деле выбрасываются вместо более общего класса Exception или NullPointerException.

Рефакторинг тестов

Извлечение дублирования — хорошая практика и для тестов; целый ряд шаблонов поможет вам организовать тестовый код, если вам не хватает фантазии:

  • базовые тесты (которые будут масштабироваться только до 1-2 родительских классов).
  • Внешние объекты, такие как шаблон FixtureLoader, для создания поддельного экземпляра базы данных и его заполнения.
  • Ограничение объектов для проверки результатов или аргументов, передаваемых в макеты.
  • Утверждение библиотеки.
  • Строители объектов для настройки сложных объектов или графиков.

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

Неполные вещи

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

В PHP тест может быть передан как неполный с помощью $ this-> markTestIncomplete (), в то время как в Java вы можете просто вызвать исключение, в зависимости от вашей тестовой среды.

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

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

Рефакторинг и идеи для его изучения должны идти в блокноте: по определению вы не можете написать тест на рефакторинг. Я использую некоторые разделы, такие как: следующие сценарии, которые еще не являются тестами, или неполные тесты и возможные варианты сценария (счастливый путь, случаи ошибок, уведомления), рефакторинг для выполнения, задачи инфраструктуры (такие как вставка X в сборку, выполнение всплеск с MongoDB.)

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