Что не тестировать в Rails?

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

Возьмите это полностью действие ванильного контроллера, например:

def create
  @event = Event.new(params[:event])
  if @event.save
    flash[:notice] = "Event successfully created."
    redirect_to events_path
  else
    render :action => 'new'
  end
end

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

То же самое можно сказать и о простых методах модели. Например:

def full_name
  "#{first_name} #{last_name}"
end

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

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

Но, как я уже сказал, я вряд ли эксперт-тестер. Я не обязательно выступаю за меньшее покрытие теста. Скорее, я ищу совет экспертов. Есть ли веская причина писать такие простые тесты?

Ответы

Ответ 1

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

Для меня тесты облегчают три вещи:

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

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

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

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

Ответ 2

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

В примере full_name, почему вы разместили пробел между ними и упорядочивали по первому имени, а затем last_name - это важно? Если позже вас попросят сортировать по фамилии, нормально ли менять порядок и добавить запятую? Что, если фамилия - это два слова - будет ли лишнее пространство влиять на вещи? Может быть, у вас также есть xml-канал, который кто-то разбирает? Если вы не уверены, что тестировать, для простой недокументированной функции, возможно, подумайте о функциональности, подразумеваемой именем метода.

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

Некоторые люди используют подход к проверке крайних случаев и предполагают, что основные функции будут разработаны с помощью использования. Принимая во внимание getter/setters, я бы хотел, чтобы какой-то модельный класс, у которого есть несколько тестов для этих методов, возможно, тестируйте диапазоны типов столбцов базы данных. Это, по крайней мере, говорит мне, что сеть в порядке, соединение с базой данных может быть выполнено, у меня есть доступ к записи в существующую таблицу и т.д. Страницы приходят и уходят, поэтому не учитывайте, что загрузка страницы является заменой фактической unit test. (Обратите внимание, что при автоматическом тестировании на основе временной метки обновления файла (автотест) этот тест не запускается, и вы хотите узнать как можно скорее)

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

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

100% охват не должен быть вашей целью - хорошее тестирование должно быть. Было бы неверно думать, что один тест на регулярное выражение выполняет что-либо. Я бы предпочел не испытывать ни одного теста, потому что мой отчет о автоматическом покрытии напоминает мне, что RE ненадежен.

Ответ 3

Единственное абсолютное правило заключается в том, что тестирование должно быть экономичным.

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

Раздел

  • Не проверяйте частные методы напрямую, оценивайте их эффекты косвенно через общедоступные методы, которые их называют.
  • Не тестировать внутренние состояния
  • Проверять нетривиальные методы, где разные контексты могут получать разные результаты (вычисления, конкатенация, регулярные выражения, ветки...)
  • Не оценивайте, что вам не нравится, например. полная копия на некотором сообщении или бесполезные части сложных структур данных, возвращаемых API...
  • Заблокируйте все элементы в модульных тестах, они называются модульными тестами, потому что вы тестируете только один класс, а не его соавторы. С помощью заглушек/шпионов вы проверяете отправленные вами сообщения, не проверяя их внутреннюю логику.
  • Рассмотрим частные вложенные классы как частные методы

Интеграция

  • Не пытайтесь протестировать все комбинации в тестах интеграции.. Для каких модульных тестов. Просто проверьте счастливые пути или наиболее распространенные случаи.
  • Не используйте Cucumber, если вы действительно не BDD
  • Тесты интеграции не всегда нужно запускать в браузере. Чтобы протестировать больше случаев с меньшим ударом производительности, некоторые тесты интеграции могут взаимодействовать непосредственно с классами моделей.
  • Не тестируйте то, что у вас нет.. Тесты интеграции должны ожидать, что сторонние зависимости выполнят свою работу, но не заменяют их собственным набором тестов.

Controller

  • В тестах контроллера проверяйте логику контроллера: перенаправление, аутентификация, разрешения, статус HTTP. Заблокируйте бизнес-логику. Рассмотрите фильтры и т.д., Как частные методы в модульных тестах, протестированные только с помощью действий открытого контроллера.

Другие

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

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

Подробнее о который опубликовал.

Ответ 4

Основным преимуществом, которое вы получили от написания unit test или двух для этого метода, будет регрессионное тестирование. Если в какой-то момент в будущем что-то изменилось, что негативно повлияло на этот метод, вы бы смогли его поймать.

Независимо от того, стоило ли вам этого, в конечном счете, вам.

Второе преимущество, которое я могу видеть, глядя на него, будет проверять крайние случаи, например, что он должен делать, если last_name - "" или nil. Это может показать неожиданное поведение.

(т.е. если last_name - nil, а first_name - "John", вы получаете full_name = > "John ")

Опять же, выигрыш по стоимости в конечном счете зависит от вас.

Ответ 5

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

Для вашего конкретного метода это выглядит как эквивалент сеттера (немного с тех пор, как я сделал Ruby on Rails) - тестирование этого метода будет проверять языковые функции. Если вы меняете значения или генерируете выходные данные, тогда вы должны пройти тест. Но если вы просто устанавливаете значения или возвращаете что-то без каких-либо вычислений или логики, я не вижу преимуществ, связанных с тем, чтобы тесты охватывали эти методы, как если бы они были неправильными, вы должны иметь возможность обнаруживать проблему при визуальной проверке или в проблеме является дефектом языка.

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