Я склонен использовать перед блоками для установки переменных экземпляра. Затем я использую эти переменные в своих примерах. Я недавно наткнулся let()
. Согласно документации RSpec, он используется для
... чтобы определить запомненный вспомогательный метод. Значение будет кэшировано для нескольких вызовов в одном и том же примере, но не во всех.
Чем это отличается от использования переменных экземпляра в до блоков? А также, когда вы должны использовать let()
против before()
?
Ответы:
Я всегда предпочитаю
let
переменную экземпляра по нескольким причинам:nil
, что может привести к незначительным ошибкам и ложным срабатываниям. Так какlet
создает метод, вы получите,NameError
когда вы неправильно его напишите, что я считаю предпочтительным. Это также облегчает рефакторинг спецификаций.before(:each)
Крючок будет работать перед каждым , например, даже если пример не использует какой - либо из переменных экземпляра , определенных в крюке. Обычно это не имеет большого значения, но если установка переменной экземпляра занимает много времени, то вы тратите впустую циклы. Для метода, определенного параметромlet
, код инициализации выполняется только в том случае, если его вызывает пример.@
).let
сохранением моегоit
блока красивым и коротким.Связанную ссылку можно найти здесь: http://www.betterspecs.org/#let
источник
let
для определения всех зависимых объектов иbefore(:each)
для настройки необходимой конфигурации или любых макетов / заглушек, необходимых в примерах. Я предпочитаю это одному большому крючку, содержащему все это. Кроме того,let(:foo) { Foo.new }
менее шумно (и более точно)before(:each) { @foo = Foo.new }
. Вот пример того, как я его использую: github.com/myronmarston/vcr/blob/v1.7.0/spec/vcr/util/…NoMethodError
предупреждение, но YMMV.foo = Foo.new(...)
пользователи, а затем пользователиfoo
. Позже вы напишите новый пример в той же группе примеров, который также должен быть созданFoo
таким же образом. На этом этапе вы хотите провести рефакторинг для устранения дублирования. Вы можете удалитьfoo = Foo.new(...)
строки из ваших примеров и заменить ихlet(:foo) { Foo.new(...) }
без изменения способа использования примеровfoo
. Но если вы сделаете рефакторинг,before { @foo = Foo.new(...) }
вам также придется обновить ссылки в примерах сfoo
на@foo
.Разница между использованием экземплярами переменных и в
let()
том , чтоlet()
является ленивой-оценено . Это означает, чтоlet()
он не оценивается, пока метод, который он определяет, не будет запущен в первый раз.Разница между
before
и вlet
том, что этоlet()
дает вам хороший способ определения группы переменных в «каскадном» стиле. При этом спецификация выглядит немного лучше за счет упрощения кода.источник
let
если вам нужно что-то оценивать каждый раз? Например, мне нужно, чтобы дочерняя модель присутствовала в базе данных, прежде чем какое-либо поведение будет запущено в родительской модели. Я не обязательно ссылаюсь на эту дочернюю модель в тесте, потому что я тестирую поведение родительских моделей. В данный момент я используюlet!
метод вместо этого, но, возможно, было бы более явным добавить эту настройкуbefore(:each)
?Я полностью заменил все использования переменных экземпляра в моих тестах rspec на использование let (). Я написал пример для друга, который использовал его для обучения небольшому классу Rspec: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html
Как говорится в некоторых других ответах, let () вычисляется лениво, поэтому он будет загружать только те из них, которые требуют загрузки. Это сушит спецификации и делает его более читабельным. На самом деле я перенес код Rspec let () для использования в моих контроллерах, в стиле gem длятекапак. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html
Наряду с отложенной оценкой, другим преимуществом является то, что в сочетании с ActiveSupport :: Concern и спецификацией / поддержкой / поведением загрузки всего, что вы можете, вы можете создать свой собственный спецификационный мини-DSL, специфичный для вашего приложения. Я написал их для тестирования ресурсов Rack и RESTful.
Стратегия, которую я использую, - «Фабрика-все» (через Машинист + Подделка / Подделка) Однако его можно использовать в сочетании с блоками before (: each) для предварительной загрузки фабрик для всего набора групп примеров, что позволяет спецификациям работать быстрее: http://makandra.com/notes/770-taking-advantage -of-RSpec-втор-пусть-в-перед тем , блоки
источник
# spec/friendship_spec.rb
и# spec/comment_spec.rb
примера, вы не думаете, что они делают его менее читабельным? Я понятия не имею, откудаusers
взяться, и нужно будет копать глубже.let()
последние пару дней, и лично я не вижу разницы, за исключением первого упомянутого преимущества Мирон. И я не уверен в том, чтобы отпустить, а что нет, может быть, потому что я ленивый и мне нравится смотреть код заранее, не открывая еще один файл. Спасибо за ваши комментарии.Важно помнить, что let оценивается лениво и не содержит в себе методов побочных эффектов, иначе вы не сможете легко перейти от let к before (: каждому) . Вы можете использовать пусть! вместо того, чтобы позволить так, чтобы это оценивалось перед каждым сценарием.
источник
В целом,
let()
это более приятный синтаксис, и он экономит на наборе@name
символов повсюду. Но будьте бдительны! Яlet()
также обнаружил, что вводит тонкие ошибки (или, по крайней мере, царапины на голове), потому что переменная на самом деле не существует, пока вы не попытаетесь ее использовать ... Скажите знак сказки: если добавлениеputs
после,let()
чтобы увидеть, что переменная верна, позволяет спецификации пройти, но безputs
спецификации не удается - вы нашли эту тонкость.Я также обнаружил, что
let()
, похоже, не кэшируется при любых обстоятельствах! Я написал это в своем блоге: http://technicaldebt.com/?p=1242Может это только у меня?
источник
let
всегда запоминает значение на время одного примера. Он не запоминает значение в нескольких примерах.before(:all)
напротив, позволяет повторно использовать инициализированную переменную в нескольких примерах.let!
она предназначена. relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/…пусть является функциональным, так как по сути это Proc. Также его кешируется.
Одна ошибка, которую я нашел сразу с помощью let ... В блоке Spec, который оценивает изменение.
Вы должны быть уверены, что позвоните
let
за пределами ожидаемого блока. то есть вы звонитеFactoryGirl.create
в своем блоке let. Я обычно делаю это, проверяя объект сохраняется.В противном случае, когда
let
блок вызывается в первый раз, изменение в базе данных фактически произойдет из-за ленивого создания экземпляра.Обновить
Просто добавляю заметку. Будьте осторожны, играя в гольф с кодом или в этом случае с этим ответом.
В этом случае мне просто нужно вызвать метод, на который отвечает объект. Поэтому я
_.persisted?
вызываю метод _ для объекта как истинный. Все, что я пытаюсь сделать, это создать экземпляр объекта. Вы могли бы назвать пустым? или ноль? тоже. Дело не в тесте, а в том, чтобы оживить объект, вызвав его.Таким образом, вы не можете рефакторинг
быть
как объект не был создан ... его ленивый. :)
Обновление 2
рычаг пусть! синтаксис для мгновенного создания объекта, который должен полностью избежать этой проблемы. Заметьте, однако, что это побеждает многие цели лени без взрыва.
Также в некоторых случаях вы можете использовать синтаксис субъекта вместо let, поскольку это может дать вам дополнительные опции.
источник
Примечание для Джозефа - если вы создаете объекты базы данных в
before(:all)
них, они не будут захвачены в транзакции, и вы с большей вероятностью оставите в своей тестовой базе данных бесполезность. Используйтеbefore(:each)
вместо этого.Другая причина использовать let и его ленивую оценку заключается в том, что вы можете взять сложный объект и протестировать отдельные части, переопределив let в контекстах, как в этом очень надуманном примере:
источник
«до» по умолчанию подразумевается
before(:each)
. Ссылка на книгу Rspec, авторское право 2010, стр. 228.Я использую
before(:each)
для заполнения некоторые данные для каждой группы примеров без необходимости вызыватьlet
метод для создания данных в блоке «it». В этом случае меньше кода в блоке "it".Я использую,
let
если я хочу некоторые данные в некоторых примерах, но не другие.И до, и пусть отлично подходят для сушки блоков "it".
Чтобы избежать путаницы, «пусть» - это не то же самое, что
before(:all)
. «Let» переоценивает свой метод и значение для каждого примера («it»), но кэширует значение по нескольким вызовам в одном и том же примере. Вы можете прочитать больше об этом здесь: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-letисточник
Здесь несогласный голос: после 5 лет rspec мне не
let
очень нравится .1. Ленивая оценка часто сбивает с толку настройку теста
Становится трудно рассуждать о настройке, когда некоторые вещи, которые были объявлены в настройке, фактически не влияют на состояние, в то время как другие.
В конце концов, из-за разочарования кто-то просто переключается
let
наlet!
(то же самое без ленивой оценки), чтобы заставить их спецификацию работать. Если это работает для них, рождается новая привычка: когда новая спецификация добавляется в более старый набор, и она не работает, первое , что пытается сделать писатель, - это добавлять удары в случайныеlet
вызовы.Довольно скоро все преимущества производительности исчезли.
2. Специальный синтаксис необычен для пользователей не rspec
Я бы лучше научил Ruby моей команде, чем уловкам rspec. Переменные экземпляра или вызовы методов полезны везде в этом и других проектах,
let
синтаксис будет полезен только в rspec.3. «Преимущества» позволяют нам легко игнорировать хорошие изменения дизайна
let()
хорош для дорогих зависимостей, которые мы не хотим создавать снова и снова. Это также хорошо сочетается сsubject
, позволяя вам высушить повторные вызовы методов с несколькими аргументамиДорогие зависимости повторялись много раз, и методы с большими сигнатурами - это те точки, где мы могли бы улучшить код:
Во всех этих случаях я могу устранить симптомы сложных тестов с помощью успокаивающего бальзама с магией rspec или попытаться устранить причину. Я чувствую, что последние несколько лет я потратил слишком много времени на первое, и теперь я хочу немного лучшего кода.
Чтобы ответить на оригинальный вопрос: я бы предпочел не делать этого, но я все еще использую
let
. я основном использую его, чтобы соответствовать стилю остальной части команды (кажется, что большинство программистов на Rails в мире сейчас глубоко знакомы с магией rspec, что происходит очень часто). Иногда я использую его, когда добавляю тест в какой-то код, который я не могу контролировать или у которого нет времени на рефакторинг для лучшей абстракции: то есть, когда единственным вариантом является болеутоляющее средство.источник
Я использую
let
для проверки своих ответов HTTP 404 в своих спецификациях API, используя контексты.Для создания ресурса я использую
let!
. Но для хранения идентификатора ресурса я используюlet
. Посмотрите, как это выглядит:Это сохраняет спецификации чистыми и читаемыми.
источник