В чем разница между 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 для ассоциаций, как указано выше?
ruby-on-rails
unit-testing
rspec
крийсна
источник
источник
Ответы:
Люди, кажется, объяснили некоторые из основных способов, которыми они отличаются, но упустили из виду
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 заключается в том, что вы получаете явную ссылку на эту переменную, вместо того, чтобы возвращаться к переменным экземпляра.Как и в случае с
let
блоками, если несколькоlet!
блоков определены с одним и тем же именем, при выполнении будет использоваться самый последний. Основное различие состоит в том, что при таком использованииlet!
блоки будут выполняться несколько раз, тогда какlet
блок будет выполняться только в последний раз.перед (: каждый) блоками
before(:each)
по умолчанию используется перед блоком, и поэтому на него можно ссылаться,before {}
а не указывать полныйbefore(:each) {}
каждый раз.Я лично предпочитаю использовать
before
блоки в нескольких основных ситуациях. Я буду использовать блоки before, если:before { get :index }
). Хотя вы можете использоватьsubject
это во многих случаях, иногда это кажется более явным, если вам не нужна ссылка.Если вы обнаружите, что пишете большие
before
блоки для своих спецификаций, проверьте свои фабрики и убедитесь, что вы полностью понимаете особенности и их гибкость.перед (: все) блоки
Они выполняются только один раз перед спецификациями в текущем контексте (и его дочерних элементах). Их можно использовать с большим преимуществом, если они написаны правильно, поскольку в определенных ситуациях это может сократить время выполнения и усилия.
Один из примеров (который вообще вряд ли повлияет на время выполнения) - это имитация переменной ENV для теста, которую вам нужно сделать только один раз.
Надеюсь, это поможет :)
источник
before(:all)
опция не существует в Minitest. Вот некоторые обходные пути в комментариях: github.com/seattlerb/minitest/issues/61its
больше не входит в состав rspec-core. Более современный идиоматический способit { is_expected.to be_awesome }
.Я предпочитаю почти всегда
let
. Сообщение, на которое вы ссылаетесь, указывает, чтоlet
это также быстрее. Однако иногда, когда нужно выполнить много команд, я мог бы использовать,before(:each)
потому что его синтаксис более ясен, когда задействовано много команд.В вашем примере я определенно предпочел бы использовать
let
вместоbefore(:each)
. Вообще говоря, когда выполняется лишь некоторая инициализация переменной, я предпочитаю использоватьlet
.источник
Большое отличие, о котором не упоминалось, заключается в том, что переменные, определенные с помощью
let
, не создаются, пока вы не вызовете его в первый раз. Таким образом, хотяbefore(:each)
блок будет создавать экземпляры всех переменных,let
позвольте вам определить количество переменных, которые вы можете использовать в нескольких тестах, он не создает их автоматически. Не зная этого, ваши тесты могут вернуться, чтобы укусить себя, если вы ожидаете, что все данные будут загружены заранее. В некоторых случаях вы даже можете определить несколькоlet
переменных, а затем использоватьbefore(:each)
блок для вызова каждогоlet
экземпляра, чтобы убедиться, что данные доступны для начала.источник
let!
для определения методов, вызываемых перед каждым примером. См. Документацию RSpec .Похоже, вы используете Машинист. Остерегайтесь, вы можете увидеть некоторые проблемы с make! внутри let (версия без взрыва), происходящая вне глобальной транзакции фикстуры (если вы также используете транзакционные фикстуры), таким образом искажая данные для других ваших тестов.
источник