it-swarm.com.ru

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

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

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

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

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

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

Я изучил это сообщение от stackoverflow

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

85
kriysna

Кажется, что люди объяснили некоторые основные отличия, но не указали before(:all) и не объяснили, почему их следует использовать.

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

пусть блокирует

Код в блоке 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! выполняются в том порядке, в котором они определены (очень похоже на блок before). Основное отличие от блоков before заключается в том, что вы получаете явную ссылку на эту переменную, а не на необходимость обращаться к переменным экземпляра.

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

до (: каждый) блоков

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

Я лично предпочитаю использовать блоки before в нескольких основных ситуациях. Я буду использовать перед блоками, если:

  • Я использую насмешки, окурки или двойники
  • Существуют разумные настройки (обычно это признак того, что ваши заводские черты не были правильно настроены)
  • Есть ряд переменных, на которые мне не нужно ссылаться напрямую, но они необходимы для настройки
  • Я пишу тесты функциональных контроллеров в Rails и хочу выполнить конкретный запрос для тестирования (т.е. before { get :index }). Несмотря на то, что вы могли бы использовать subject для этого во многих случаях, иногда это кажется более явным, если вам не нужна ссылка.

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

до (: все) блоки

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

Одним из примеров (который вряд ли повлияет на время выполнения) является макетирование переменной ENV для теста, который вам нужно выполнить только один раз.

Надеюсь, это поможет :)

134
Jay

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

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

25
Spyros

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

15
mikeweber

Похоже, вы используете Машинист. Осторожно, вы можете увидеть некоторые проблемы с make! внутри let (версия без взрыва) происходит за пределами глобальной транзакции фикстуры (если вы также используете транзакционные фикстуры), что приводит к повреждению данных для других ваших тестов.

3
Tim Connor