Ответ 1
Люди, похоже, объяснили некоторые из основных способов, которыми они отличаются, но не учтены до (: все) и не объясняют, почему именно они должны использоваться.
Я полагаю, что переменные экземпляра не имеют места в подавляющем большинстве спецификаций, отчасти из-за причин, упомянутых в этом ответе, поэтому я не буду упоминайте их как вариант здесь.
let blocks
Код внутри блока let
выполняется только при наличии ссылки, ленивая загрузка означает, что упорядочение этих блоков не имеет значения. Это дает вам большую мощность, чтобы сократить повторную настройку через ваши спецификации.
Один (чрезвычайно надуманный и маленький) пример:
let(:person) { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }
context 'with a moustache' do
let(:has_moustache) { true }
its(:awesome?) { should be_true }
end
context 'without a moustache' do
let(:has_moustache) { false }
its(:awesome?) { should be_false }
end
Вы можете видеть, что has_moustache
определяется по-разному в каждом случае, но нет необходимости повторять определение subject
. Важно отметить, что будет использоваться последний блок let
, определенный в текущем контексте. Это полезно для установки по умолчанию для большинства спецификаций, которые могут быть перезаписаны, если необходимо.
Например, проверка возвращаемого значения calculate_awesome
, если передана модель person
с top_hat
, установлена в true, но усы не будут:
context 'without a moustache but with a top hat' do
let(:has_moustache) { false }
let(:person) { build(:person, top_hat: true) }
its(:awesome?) { should be_true }
end
Еще одна вещь, которая стоит отметить о блоках let, они не должны использоваться, если вы ищете что-то, что было сохранено в базе данных (т.е. Library.find_awesome_people(search_criteria)
), поскольку они не будут сохранены в базе данных, если они уже не были ссылки. Блоки let!
или before
должны использоваться здесь.
Кроме того, никогда никогда использовать before
для запуска выполнения let
блоков, это то, что let!
сделано для!
пусть! блоки
let!
блоки выполняются в том порядке, в котором они определены (как и перед блоком). Одно основное отличие перед блоками состоит в том, что вы получаете явную ссылку на эту переменную, вместо необходимости возвращаться к переменным экземпляра.
Как и в случае блоков let
, если несколько блоков let!
определены с тем же именем, самое последнее - это то, что будет использоваться при выполнении. Основное различие заключается в том, что блоки let!
будут выполняться несколько раз, если они используются как это, тогда как блок let
будет выполнять только последний раз.
before (: each) блокирует
before(:each)
является значением по умолчанию перед блоком, поэтому его можно называть как before {}
, а не указывать полный before(:each) {}
каждый раз.
Я предпочитаю использовать before
блоки в нескольких основных ситуациях. Я буду использовать до блоков, если:
- Я использую насмешку, stubbing или doubleles
- Существует какая-то разумная настройка размера (обычно это знак, что ваши черты factory установлены неправильно).
- Есть ряд переменных, которые мне не нужно ссылаться напрямую, но требуются для установки
- Я пишу тесты функционального контроллера в рельсах, и я хочу выполнить конкретный запрос для тестирования (т.е.
before { get :index }
). Даже если вы можете использоватьsubject
для этого во многих случаях, иногда он становится более явным, если вам не нужна ссылка.
Если вы обнаружите, что пишете большие блоки before
для своих спецификаций, проверьте свои заводы и убедитесь, что вы полностью понимаете черты и их гибкость.
до (: all) блоков
Они выполняются только один раз, перед спецификациями в текущем контексте (и его дочерних элементах). Они могут быть использованы с большим преимуществом, если они написаны правильно, так как есть определенные ситуации, которые могут сократить выполнение и усилия.
Один пример (который вряд ли повлияет на время выполнения на всех) - высмеивает переменную ENV для теста, которую вам нужно только когда-либо делать.
Надеюсь, что помогает:)