http://betterspecs.org/#subject содержит информацию о subject
и let
. Тем не менее, я все еще неясен в отношении разницы между ними. Кроме того, SO post Каков аргумент против использования до, пусть и субъект в тестах RSpec? сказал, что лучше не использовать либо subject
, либо let
. Куда мне идти? Я так растерялся.
Какая разница между предметом RSpec и пусть? Когда они должны использоваться или нет?
Ответ 1
Сводка: объект RSpec - это специальная переменная, которая относится к тестируемому объекту. Ожидания могут быть установлены на нем неявно, что поддерживает однострочные примеры. Это ясно для читателя в некоторых идиоматических случаях, но в противном случае трудно понять и его следует избегать. RSpec let
переменные - это только ленивые экземпляры (memoized). Они не так трудно следовать как субъект, но все же могут привести к запутанным испытаниям, поэтому их следует использовать с усмотрением.
Объект
Как это работает
Объектом является проверяемый объект. RSpec имеет явное представление о предмете. Это может быть или не быть определено. Если это так, RSpec может вызывать методы на нем, не ссылаясь на него явно.
По умолчанию, если первый аргумент для группы внешнего кода (describe
или context
block) является классом, RSpec создает экземпляр этого класса и назначает его субъекту. Например, следующие проходы:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
Вы можете сами определить тему subject
:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Вы можете указать субъекту имя при его определении:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
Даже если вы назовете тему, вы все равно можете ссылаться на нее анонимно:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
Вы можете определить более одного именованного объекта. Самый последний определенный именованный объект - анонимный subject
.
Однако предмет определен,
-
Он создавался лениво. То есть, неявное создание экземпляра описанного класса или выполнение блока, переданного в
subject
, происходит не до тех пор, покаsubject
или названный объект не будет указан в примере. Если вы хотите, чтобы ваш эксплицитный объект был запрограммирован с нетерпением (перед тем, как пример в его группе работает), скажитеsubject!
вместоsubject
. -
Ожидания могут быть установлены на нем неявно (без записи
subject
или имени именованного объекта):describe A do it { is_expected.to be_an(A) } end
Объект существует для поддержки этого однострочного синтаксиса.
Когда использовать его
Неявный subject
(вывод из группы примеров) трудно понять, потому что
- Он был создан за кулисами.
- Является ли он неявным (путем вызова
is_expected
без явного получателя) или явно (какsubject
), он не дает читателю никакой информации о роли или природе объекта, на котором вызывается ожидание. - В синтаксисе примера с одним слоем нет описания примера (строковый аргумент
it
в синтаксисе нормального примера), поэтому единственная информация, которую читатель имеет в своем примере, - это само ожидание.
Поэтому полезно использовать неявный объект, когда контекст, вероятно, будет хорошо понят всеми читателями, и нет необходимости в описании примера. Канонический случай проверяет проверки ActiveRecord с помощью сокетов-кандидатов:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
Анонимный анонимный subject
(определенный с помощью subject
без имени) немного лучше, потому что читатель может увидеть, как он создан, но
- он все равно может помещать экземпляр объекта далеко не там, где он использовался (например, в верхней части примерной группы со многими примерами, которые его используют), что все еще сложно выполнить, и
- у него есть другие проблемы, которые неявный субъект делает.
Именованный объект предоставляет имя, указывающее намерение, но единственная причина использовать именованный объект вместо переменной let
- это если вы хотите использовать анонимный объект некоторое время, и мы просто объяснили, почему анонимный предмет трудно понять.
Таким образом, законные использования явного анонимного subject
или именованного объекта очень редки.
let
переменные
Как они работают
let
переменные похожи на именованные объекты, за исключением двух отличий:
- они определяются с помощью
let
/let!
вместоsubject
/subject!
- они не устанавливают анонимный
subject
или позволяют ожидать, что ожидания будут вызваны на него неявно.
Когда использовать их
Полностью законно использовать let
для уменьшения дублирования среди примеров. Однако сделайте это только тогда, когда оно не принесет жертвы ясности теста. Самое безопасное время для использования let
- это когда цель переменной let
полностью очищена от ее имени (так что читателю не нужно найти определение, которое может быть много строк, чтобы понять каждый пример), и он используется таким же образом в каждом примере. Если любая из этих вещей неверна, рассмотрите определение объекта в простой старой локальной переменной или вызовите метод factory прямо в примере.
let!
является рискованным, потому что он не ленив. Если кто-то добавляет пример в группу примеров, содержащую let!
, но для примера не требуется переменная let!
- этот пример будет трудно понять, потому что читатель увидит переменную
let!
и задается вопросом, влияет ли и как это влияет на пример - пример будет медленнее, чем нужно, из-за времени, затраченного на создание
let!
variablle
Поэтому используйте let!
, если вообще, только в небольших простых группах примеров, где менее вероятно, что будущие авторы сценариев попадут в эту ловушку.
Фетиш с одним ожиданием на пример
Существует обычное чрезмерное использование предметов или переменных let
, которые стоит обсудить отдельно. Некоторым людям нравится их использовать следующим образом:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(Это простой пример метода, который возвращает число, для которого нам нужны два ожидания, но этот стиль может иметь гораздо больше примеров/ожиданий, если метод возвращает более сложное значение, которое требует многих ожиданий и/или имеет много побочные эффекты, которые все требуют ожиданий.)
Люди делают это, потому что слышали, что на пример должно быть только одно ожидание (которое смешивается с действующим правилом, которое нужно проверять только на один вызов метода на пример) или потому, что они влюблены в сложность RSpec, Не делайте этого, будь то анонимный или именованный объект или переменная let
! Этот стиль имеет несколько проблем:
- Анонимный субъект не является предметом примеров - метод является предметом. Написание теста таким образом закручивает язык, что затрудняет мысль о нем.
- Как всегда с примерами из одной строки, нет смысла объяснять смысл ожиданий.
- Объект должен быть построен для каждого примера, который медленный.
Вместо этого напишите один пример:
describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end
Ответ 2
Subject
и let
- это просто инструменты, которые помогут вам убрать и ускорить ваши тесты. Люди в сообществе rspec используют их, поэтому я не буду беспокоиться о том, можно ли их использовать или нет. Они могут использоваться аналогичным образом, но служат в несколько разных целях.
Subject
позволяет вам объявить объект теста, а затем повторно использовать его для любого количества последующих тестовых примеров. Это уменьшает повторение кода (DRYing вверх по вашему коду)
let
является альтернативой блокам before: each
, которые присваивают тестовые данные переменным экземпляра. let
дает вам несколько преимуществ. Во-первых, он кэширует значение, не присваивая его переменной экземпляра. Во-вторых, он лениво оценивается, а это значит, что он не оценивается до тех пор, пока спецификация не потребует его. Таким образом, let
помогает ускорить ваши тесты. Я также думаю, что let
легче читать
Ответ 3
subject
- это то, что тестируется, обычно это экземпляр или класс. let
предназначен для назначения переменных в ваших тестах, которые оцениваются лениво и с использованием переменных экземпляра. В этом потоке есть несколько хороших примеров.