Статьи

Основы AntiPatterns: Rails-тесты

Эта статья представляет собой введение в расширенные возможности тестирования AntiPatterns в Rails. Если вы новичок в Test-Driven-Development и хотите получить несколько очень полезных рекомендаций, эта статья была написана именно для вас.

  • Позволять
  • Тайные гости
  • Неясные тесты
  • Медленные тесты
  • арматура
  • Хрупкие тесты
  • Атрибуты данных
1
2
3
4
5
6
7
8
describe Mission do
 
  let(:james_bond) { build_stubbed(:agent, name: ‘James Bond’, number: ‘007’) }
  let(:mission) { build_stubbed(:mission, title: ‘Moonraker’) }
 
 
end

Метод let helper в RSpec очень часто используется для создания переменных экземпляра, которые доступны между несколькими тестами. Будучи активным студентом практики TDD, вы, вероятно, написали свою справедливую долю из них, но следование этой практике может легко привести к появлению множества загадочных гостей — см. Ниже — что, безусловно, не является тем, что нам нужно, чтобы разрушить нашу вечеринку!

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

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

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

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

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

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

Если вы начнете идти по пустому пути, вы часто будете получать довольно толстые объекты, которые пытаются сделать множество тестов одновременно счастливыми. Конечно, вы можете создать множество вариаций этих пустяков, но я думаю, что это делает их немного неактуальными. Почему бы не пойти еще дальше, не допустить и положиться на Ruby без магии RSpec DSL?

Я больше нахожусь в лагере за то, чтобы быть на стороне повторяющегося кода установки для каждого теста, чем быть слишком СУХОЙ, неясной или загадочной в моем наборе тестов. Я бы всегда пошел на большую читабельность. Метод тестирования должен прояснить причину и следствие его участвующих частей — использование коллабораторов объектов, которые, возможно, определены далеко от вашего теста, не в ваших интересах. Если вам нужно извлечь что-то, используйте выразительные методы, которые инкапсулируют эти знания.

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

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

Результатом станут неясные тесты, которые потребуют от вас полного перехода в режим Шерлока Холмса. Я думаю, это звучит куда веселее, чем есть. Итог, это пустая трата времени каждого.

Тайные гости ставят два проблемных вопроса:

  • Откуда этот объект?
  • Из чего именно он состоит?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
describe Mission do
 
  let(:agent_01) { build_stubbed(:agent, name: ‘James Bond’, number: ‘007’) }
  let(:agent_02) { build_stubbed(:agent, name: ‘Moneypenny’, number: ‘243’) }
  let(:title) { ‘Moonraker’ }
  let(:mission) { build_stubbed(:mission, title: title) }
  mission.agents << agent_01 << agent_02
 
  …
 
  …
 
  …
 
  #lots of other tests
 
  describe ‘#top_agent’ do
    it ‘returns highest ranking agent associated to a mission’ do
 
      expect(mission.top_agent).to eq(‘James Bond’)
    end
  end
end

В этом блоке описания для #top_agent не хватает ясности и контекста. Какой агент задействован и о какой миссии мы говорим здесь? Это заставляет разработчиков охотиться за объектами, которые внезапно появляются в ваших тестах.

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

Решение довольно простое: вам нужны свежие «приспособления» и создавать локальные версии объектов с точными данными, которые вам нужны — и не более того! Factory Girl — хороший выбор для этого.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
describe Mission do
 
  #…
 
  #…
 
  #…
 
  #lots of other tests
 
  describe ‘#top_agent’ do
    it ‘returns a list of all agents associated to a mission’ do
      agent_01 = build_stubbed(:agent, name: ‘James Bond’, number ‘007’)
      agent_02 = build_stubbed(:agent, name: ‘Moneypenny’, number ‘243’)
      mission = build_stubbed(:mission, title: ‘Moonraker’)
      mission.agents << agent_01 << agent_02
 
      expect(mission.top_agent).to eq(‘James Bond’)
    end
  end
end

Приведенный выше пример строит все объекты, необходимые для наших тестов, в реальном тестовом примере и предоставляет весь требуемый контекст. Разработчик может сосредоточиться на конкретном контрольном примере и ему не нужно «загружать» другой — возможно, совершенно не связанный — контрольный пример для решения данной ситуации. Нет больше безвестности!

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

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

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

01
02
03
04
05
06
07
08
09
10
 
 context «agent status» do
   it «returns the status of the mission’s agent» do
     double_o_seven = build_stubbed(:agent)
     mission = build_stubbed(:mission, agent: double_o_seven)
 
     expect(mission.agent_status).to eq(double_o_seven.status)
   end
 end

Мы создаем универсальный агент. Откуда мы знаем, что это 007? Мы также проверяем состояние агента, но его также нигде не найти — ни в настройке, ни в явном виде на этапе проверки в нашем заявлении о expect . Отношения между double_o_seven.status и статусом миссии могут быть запутанными, поскольку на самом деле это происходит из ниоткуда. Мы можем сделать лучше:

01
02
03
04
05
06
07
08
09
10
 
 context «agent status» do
   it «returns the status of the mission’s agent» do
     double_o_seven = build_stubbed(:agent, name: ‘James Bond’, status: ‘Missing in action’))
     mission = build_stubbed(:mission, agent: double_o_seven)
 
     expect(mission.agent_status).to eq(‘James Bond: Missing in action’)
   end
 end

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

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

Почему ваш набор тестов становится все медленнее и медленнее, хотя вы думаете, что делаете все правильно? Чувствуешь себя немного наказанным за написание тестов? Медленные тесты — отстой — большое время! Есть несколько проблем с ними. Наиболее важной проблемой является то, что медленные тесты приводят к пропуску тестов в долгосрочной перспективе. Как только вы окажетесь в точке, когда ваш набор тестов будет длиться вечно, вы будете гораздо охотнее думать самим себе: «Черт возьми, я их запусту позже! У меня есть дела поважнее, чем ждать, пока все закончится ». И вы абсолютно правы, у вас есть дела поважнее.

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

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

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

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

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

Что мы можем сделать, чтобы ускорить тесты? Здесь важны две скорости:

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

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

Создание дубликатов тестов — это хороший способ сделать ваши тесты более быстрыми, в то время как совместные объекты, необходимые для вашей настройки, будут суперфокусными и легкими. Factory Girl также предоставляет вам различные возможности для «умного» создания ваших тестовых данных. Но иногда нет никакого способа сохранить данные в базе данных (что гораздо реже, чем вы могли бы ожидать), и именно здесь вы должны провести черту. В любое другое время избегайте этого, как ад, и ваш набор тестов будет оставаться быстрым и гибким.

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

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

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

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

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

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

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

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

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

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

Если вам нравится использовать Vim, у вас есть еще одна причина потратить некоторое время на повышение эффективности использования вашего редактора. Для просмотра Vim доступно множество удобных инструментов. Я помню, что Sublime Text также предлагает запускать тесты из редактора, но кроме этого вам нужно немного изучить, чтобы выяснить, на что способен ваш выбранный редактор в этом отношении. Аргумент, который вы часто будете слышать от энтузиастов TDD, заключается в том, что вы не хотите покидать свой редактор, потому что в целом вы будете тратить на это слишком много времени. Вы хотите гораздо больше оставаться в зоне и не терять мысли, когда вы можете делать подобные вещи с помощью быстрого ярлыка из вашего редактора кода.

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

Последнее, что нужно для дороги. Используйте preloader как Spring . Вы будете удивлены, сколько времени вы сможете сбрить, если вам не нужно загружать Rails для каждого теста. Ваше приложение будет работать в фоновом режиме и не должно загружаться все время. Сделай это!

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

Приспособления базы данных ActiveRecord являются отличными примерами наличия множества гостей Mystery в вашем тестовом наборе. На заре Rails и Ruby TDD светильники YAML были стандартом де-факто для настройки тестовых данных в вашем приложении. Они сыграли важную роль и помогли продвинуть отрасль вперед. В настоящее время у них довольно плохая репутация.

1
2
3
4
5
6
7
8
9
Quartermaster:
  name: Q
  favorite_gadget: Broom radio
  skills: Inventing gizmos and hacking
 
00Agent:
  name: James Bond
  favorite_gadget: Submarine Lotus Esprit
  skills: Getting Bond Girls killed and covert infiltration

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

Один из сценариев, которого мы определенно хотим избежать, — это изменение небольших деталей существующего прибора и провал тонн тестов. Если эти неудачные тесты не связаны, ситуация еще хуже — хороший пример слишком хрупких тестов. Чтобы «защитить» существующие тесты от этого сценария, это также может привести к тому, что ваш набор приборов превысит любой разумный размер — поскольку СУХОЙ с приборами в этот момент, скорее всего, больше не будет на столе.

Чтобы избежать нарушения ваших тестовых данных, когда происходят неизбежные изменения, разработчики были рады принять более новые стратегии, которые предлагали больше гибкости и динамического поведения. Вот тут и пришла Factory Girl и поцеловала дни YAML на прощание.

Другой проблемой является сильная зависимость между тестом и файлом фиксации .yml. Поскольку приборы определены в отдельном файле .yml, загадочные гости также являются основной болью, ожидающей укуса из-за неясности. Я упоминал, что приборы импортируются в тестовую базу данных без прохождения каких-либо проверок и не придерживаются жизненного цикла Active Record? Да, это тоже не круто — с какой бы стороны вы ни посмотрели на это!

Factory Girl позволяет избежать всего этого, создавая объекты, соответствующие встроенным тестам, и только с данными, необходимыми для этого конкретного случая. Девиз: только определите минимальный минимум в ваших фабричных определениях и добавьте остальное на тестовой основе. Локально (в ваших тестах) переопределение значений по умолчанию, определенных на ваших фабриках, является гораздо лучшим подходом, чем наличие множества единорогов приспособлений, ожидающих устаревания в файле приспособлений.

Этот подход также более масштабируемый. Factory Girl предоставляет вам множество инструментов для создания всех нужных вам данных — с учетом всех нюансов, которые вам нравятся, — но также предоставляет вам множество боеприпасов, чтобы оставаться СУХИМЫМИ там, где это необходимо. Думаю, плюсы и минусы хорошо сбалансированы с этой библиотекой. Отказ от проверок также не является поводом для беспокойства. Я думаю, что использование фабричного шаблона для тестовых данных более чем разумно и является одной из основных причин, почему Factory Girl была так хорошо принята сообществом.

Сложность — это быстрорастущий враг, которого приспособления YAML вряд ли могут эффективно использовать. В некотором смысле, я думаю о светильниках как о стероидах. Вы не только размещаете их еще дальше — находясь в отдельном файле и т. Д. — вы также можете предварительно загружать больше приборов, чем вам на самом деле может понадобиться. ПОКОЙСЯ С МИРОМ!

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
describe Mission do
  let(:agent) { build_stubbed(:agent, name: ‘James Bond’, number: ‘007’) }
  let(:title) { ‘Moonraker’ }
  let(:mission) { build_stubbed(:mission, title: title) }
 
  #…
 
  #…
 
  #…
 
  #lots of other tests
 
  describe ‘#joint_operation_agent_name’ do
    let(:agent) { build_stubbed(:agent, name: ‘Felix Leiter’, agency: ‘CIA’)
    mission.agents << agent
 
    it “returns mission’s joint operation’s agent name” do
      expect(mission.joint_operation_agent_name).to eq(‘Felix Leiter’)
    end
  end
end

В этом сценарии мы четко изменили локально состояние объекта, которое было определено в нашей настройке. agent идет речь, теперь является сотрудником ЦРУ и имеет другое имя. mission снова приходит из ниоткуда. Противные вещи, правда.

Не удивительно, что другие тесты, которые, возможно, полагаются на другую версию agent начинают взрываться. Давайте избавимся от let ерунды и снова создадим нужные нам объекты там, где мы их тестируем — конечно, только с теми атрибутами, которые нам нужны для тестового примера.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
describe Mission do
 
  #…
 
  #…
 
  #…
 
  #lots of other tests
 
  describe ‘#joint_operation_agent_name’ do
    agent = build_stubbed(:agent, name: ‘Felix Leiter’, agency: ‘CIA’)
    mission = build_stubbed(:mission)
    mission.agents << agent
 
    it “returns mission’s joint operation’s agent name” do
      expect(mission.joint_operation_agent_name).to eq(‘Felix Leiter’)
    end
  end
end

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

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

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

Если в своем тесте вы жестко запрограммировали класс типа class='mission-wrapper' и умный дизайнер решит изменить это плохое имя, ваш тест будет излишне затронут. И дизайнер не виноват, конечно. Откуда она узнает, что это влияет на часть вашего набора тестов — по крайней мере, очень маловероятно.

1
2
3
4
5
6
7
<div class=’mission data-role=’single-mission’>
 
  <h2><% = @mission.agent_status %></h2>
 
  …
 
</div>
1
2
3
4
5
6
7
8
9
context «mission’s agent status» do
   it ‘does something with a mission’ do
     …
 
     …
 
     expect(page).to have_css ‘[data-role=single-mission]’
   end
 end

Мы ожидаем увидеть некоторый элемент HTML на странице и пометить его как data-role . У дизайнеров нет причин для этого, и вы защищены от хрупких испытаний, которые происходят из-за изменений в стиле моделирования.

Это довольно эффективная и полезная стратегия, которая в принципе ничего не стоит взамен. Единственное, что может понадобиться — это короткая беседа с дизайнерами. Кусок пирога!

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

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

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