RSpec: В чем разница между let и a before block?

В чем разница между let и блоком before в RSpec?

А когда использовать каждый?

Что будет хорошим подходом (пусть или до) в приведенном ниже примере?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

Я изучил этот qaru.site/info/16936/...

Но хорошо ли определять let для таких ассоциаций, как указано выше?

Ответ 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 для теста, которую вам нужно только когда-либо делать.

Надеюсь, что помогает:)

Ответ 2

Почти всегда, я предпочитаю let. Сообщение, которое вы указываете, указывает, что let также быстрее. Однако иногда, когда нужно выполнить несколько команд, я мог бы использовать before(:each), потому что его синтаксис более ясен, когда задействовано много команд.

В вашем примере я бы предпочел использовать let вместо before(:each). Вообще говоря, когда выполняется только какая-то переменная инициализации, мне нравится использовать let.

Ответ 3

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

Ответ 4

Похоже, вы используете машиниста. Опасайтесь, вы можете увидеть некоторые проблемы с make! внутри версии let (версия non-bang), происходящей за пределами глобальной транзакции fixture (если вы используете также транзакционные светильники), таким образом, искажая данные для ваших других тестов.