Описание: Субъект RSpec - это специальная переменная, которая относится к тестируемому объекту. Ожидания могут быть установлены неявно, что поддерживает однострочные примеры. В некоторых идиоматических случаях он понятен читателю, но в остальном его трудно понять, и его следует избегать. letПеременные RSpec - это просто переменные, созданные лениво (запомненные). За ними не так сложно следить, как за предметом, но они все же могут привести к запутанным тестам, поэтому их следует использовать с осторожностью.
Предмет
Как это устроено
Субъект - это проверяемый объект. RSpec имеет четкое представление о предмете. Это может или не может быть определено. Если это так, RSpec может вызывать для него методы, не обращаясь к нему явно.
По умолчанию, если первый аргумент самой внешней группы ( describeили contextблока) является классом, 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указанный объект не будет упомянут в примере. Если вы хотите, чтобы ваш объект explict был создан без промедления (до запуска примера в его группе), скажите subject!вместо subject.
Ожидания могут быть установлены неявно (без написания subjectили имени названного субъекта):
describe A do
it { is_expected.to be_an(A) }
end
Тема существует для поддержки этого однострочного синтаксиса.
Когда это использовать
Неявное subject(выведенное из группы примеров) трудно понять, потому что
- Он создается за кулисами.
- Независимо от того, используется ли он неявно (вызывая
is_expectedбез явного получателя) или явно (как subject), он не дает читателю никакой информации о роли или природе объекта, для которого вызывается ожидание.
- Синтаксис однострочного примера не имеет описания примера (строковый аргумент
itв обычном синтаксисе примера), поэтому единственная информация, которая есть у читателя о цели примера, - это само ожидание.
Поэтому использовать неявную тему полезно только тогда, когда контекст, вероятно, будет хорошо понят всем читателям и действительно нет необходимости в описании примера . Канонический случай - это тестирование валидаций ActiveRecord с помощью сопоставителей shoulda:
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переменной полностью ясно из ее имени (так что читателю не нужно искать определение, которое может быть на расстоянии многих строк, чтобы понять каждый пример), и он используется таким же образом в каждом примере. Если что-то из этого не соответствует действительности, рассмотрите возможность определения объекта в простой старой локальной переменной или вызова фабричного метода прямо в примере.
let!рискованно, потому что не лениво. Если кто-то добавляет пример в группу примеров, которая содержит let!, но в примере не требуется let!переменная,
- этот пример будет трудно понять, потому что читатель увидит
let!переменную и задастся вопросом, влияет ли она на пример и как
- пример будет медленнее, чем нужно, из-за времени, затраченного на создание
let!переменной
Так что используйте 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