Какая разница между предметом RSpec и пусть? Когда они должны использоваться или нет?

http://betterspecs.org/#subject содержит информацию о subject и let. Тем не менее, я все еще неясен в отношении разницы между ними. Кроме того, SO post Каков аргумент против использования до, пусть и субъект в тестах RSpec? сказал, что лучше не использовать либо subject, либо let. Куда мне идти? Я так растерялся.

Ответ 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 предназначен для назначения переменных в ваших тестах, которые оцениваются лениво и с использованием переменных экземпляра. В этом потоке есть несколько хороших примеров.

https://github.com/reachlocal/rspec-style-guide/issues/6