Описание: Субъект 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
:aggregate_failures
тег в строке вродеit "marks a task complete", :aggregate_failures do
(взято из книги Rails 5 Test Prescriptions)expect(result).to be_between(0, 9)
.expect(result.to_s).to match(/^[0-9]$/)
- я знаю, что это уродливо, но это действительно проверяет то, что вы говорите, или, возможно, используйтеbetween
+is_a? Integer
, но здесь вы тестируете тип тоже. И простоlet
... это не должно вызывать беспокойства, и на самом деле может быть лучше переоценить значения между примерами. В противном случае +1 за постlet!
и я не пытался убедить своих товарищей по команде в одиночку. Я пришлю этот ответ.Subject
иlet
являются просто инструментами, которые помогут вам привести в порядок и ускорить ваши тесты. Люди в сообществе rspec действительно используют их, поэтому я не буду беспокоиться о том, можно ли их использовать или нет. Их можно использовать аналогично, но они служат немного другим целям.Subject
позволяет вам объявить объект тестирования, а затем повторно использовать его для любого количества следующих тестовых случаев. Это уменьшает повторение кода (СУШКА кода)Let
является альтернативойbefore: each
блокам, которые присваивают тестовые данные переменным экземпляра.Let
дает вам несколько преимуществ. Во-первых, он кэширует значение, не присваивая его переменной экземпляра. Во-вторых, он оценивается лениво, что означает, что он не оценивается до тех пор, пока его не потребует спецификация. Таким образомlet
вы сможете ускорить ваши тесты. Я тоже думаюlet
легче читатьисточник
subject
это то, что тестируется, обычно это экземпляр или класс.let
предназначен для присвоения переменных в ваших тестах, которые оцениваются лениво по сравнению с использованием переменных экземпляра. В этой теме есть несколько хороших примеров.https://github.com/reachlocal/rspec-style-guide/issues/6
источник