it-swarm.com.ru

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

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

53
new2cpp

Резюме: Предмет RSpec - это специальная переменная, которая относится к тестируемому объекту. Ожидания могут быть установлены на нем неявно, что поддерживает однострочные примеры. Это понятно читателю в некоторых идиоматических случаях, но в противном случае его трудно понять, и его следует избегать. Переменные RSpec let являются просто ленивыми экземплярами (запоминаемыми). За ними не так сложно следить, как за предметом, но они все же могут привести к запутанным тестам, поэтому их следует использовать с осторожностью.

Предмет

Как это устроено

Предмет - это объект, который тестируется. 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.

Однако предмет определен,

  1. Это ленивый экземпляр. То есть неявное создание экземпляра описанного класса или выполнение блока, переданного в subject, не происходит до тех пор, пока subject или именованный субъект не будет упомянут в примере. Если вы хотите, чтобы ваш предмет экспликации создавался с нетерпением (до запуска примера в его группе), скажите subject! вместо subject.

  2. Ожидания могут быть установлены неявно (без записи subject или имени именованного субъекта):

    describe A do
      it { is_expected.to be_an(A) }
    end
    

    Субъект существует для поддержки этого однострочного синтаксиса.

Когда его использовать

Неявное subject (выведенное из группы примеров) трудно понять, потому что

  • Он создан за кулисами.
  • Используется ли он неявно (путем вызова is_expected без явного получателя) или явно (как subject), он не дает читателю никакой информации о роли или природе объекта, для которого вызывается ожидание.
  • Пример синтаксиса с одной строкой не имеет описания примера (строковый аргумент it в обычном синтаксисе примера), поэтому единственная информация, которую читатель имеет о цели примера, - это само ожидание.

Следовательно, полезно использовать неявный субъект только тогда, когда контекст, вероятно, будет хорошо понят всеми читателями и в действительности нет необходимости в описании примера. Канонический случай - это проверка валидаций ActiveRecord с помощью совпадений musta:

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 полностью ясна из ее name (чтобы читателю не приходилось находить определение, которое могло бы находиться в нескольких строках, чтобы понять каждый пример), и оно используется одинаково в каждом примере. Если что-то из этого не соответствует действительности, рассмотрите определение объекта в простой старой локальной переменной или вызов метода фабрики прямо в примере.

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
101
Dave Schweisguth

Subject и let - это всего лишь инструменты, которые помогут вам привести в порядок и ускорить ваши тесты. Люди в сообществе rspec используют их, поэтому я не буду беспокоиться о том, можно ли их использовать или нет. Они могут использоваться аналогично, но служат немного другим целям

Subject позволяет вам объявить объект тестирования, а затем повторно использовать его для любого количества последующих тестов. Это уменьшает повторение кода (высушивание кода)

Let является альтернативой блокам before: each, которые присваивают тестовые данные переменным экземпляра. Let дает вам пару преимуществ. Во-первых, он кэширует значение, не назначая его переменной экземпляра. Во-вторых, он лениво оценивается, что означает, что он не будет оценен, пока спецификация не вызовет его. Таким образом, let поможет вам ускорить ваши тесты. Я также думаю, что let легче читать

3
Ren

subject - это то, что тестируется, обычно это экземпляр или класс. let для назначения переменных в ваших тестах, которые оцениваются лениво по сравнению с использованием переменных экземпляра. В этой теме есть несколько хороших примеров.

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

1
nikkypx