Сколько времени «позволяет» реально сэкономить в тестах RSpec? - PullRequest
0 голосов
/ 28 декабря 2018

Мне гораздо проще просто установить переменную в моем коде, чем использовать let.let является привередливым и всегда говорит мне, как я использую его неправильно.

Когда я использую в своих спецификациях простое объявление переменных, например

tx_good = makeTransaction1(), все работает нормально.

Но когда я использую let вот так

let(:tx_good) { makeTransaction1() } Я непременно получу какую-то ошибку, подобную этой, говорящую мне, что она не может идти туда или сюда ...

  `let` and `subject` declarations are not intended to be called
   in a `before(:context)` hook, as they exist to define state that
   is reset between each example, while `before(:context)` exists to
   define state that is shared across examples in an example group.

Учитывая, насколько привередливое использование let, я вынужден задаться вопросом, стоит ли это дополнительных усилий и осторожности, которые я должен проявить, чтобы использовать его.Кто-нибудь знает, сколько времени на самом деле экономится, используя let вместо простого присвоения переменной заранее?

Я хочу следовать хорошему протоколу тестирования, поэтому я надеюсь, что кто-то сможет убедить меня в том, почему я должен использовать let, как (на первый взгляд) все остальные.

1 Ответ

0 голосов
/ 29 декабря 2018

Вы неправильно используете этот материал, и я понимаю ваше разочарование.Итак, позвольте мне дать вам краткое руководство по использованию let s в RSpec.

Основное значение при использовании let не зависит от экономии вычислительной мощности.Это неотъемлемая часть более широкой философии RSpec.Я постараюсь объяснить, и, надеюсь, вам будет легче прогрессировать ...

let лениво

все, что вы определите внутри блока, будет вызываться тогда и только тогда, когдана самом деле он используется в спецификации:

context do
  let(:foo) { sleep(10000) } # will not happen
  specify { expect(1).to eq(1) }
end 

context do 
  specify do 
     foo = sleep(10000) # you'll wait
     expect(1).to eq(1)
  end
end

Использование let!, что является нетерпеливой (то есть не ленивой) версией let

let запоминается

Все, что определено внутри блока, произойдет только один раз (в контексте контекста):

context do
  let(:random_number) { rand }
  specify do
    expect(random_number).to eq(random_number) # will always pass
  end
end

Если вы не хотите эту функцию, определите метод:

context do
  def random_number
    rand
  end
  specify do
    expect(random_number).to eq(random_number) # sometimes pass, mostly fail
  end
end

let в контекстах более низкого уровня перезаписывает let определения более высокого уровня:

context do
   let(:x) { 1 }
   specify { expect(x).to eq(1) # pass

   context 'with different x' do 
     let(:x) { 2 }
     specify { expect(x).to eq(2) # pass
   end

   context do
     specify { expect(x).to eq(1) # pass
   end
end

^ это позволяет вам составлять спецификации таким образом, где упоминаются только соответствующие «части» установкив контексте, например:

context do 
   let(:x) { 1 }
   let(:y) { 1 }
   let(:z) { 1 }
   specify { expect(foo(x, y, z)).to eq(3) }

   context 'when z is nil'
     let(:z) { nil }
     specify { expect(foo(x, y, z)).to raise_error) } # foo doesn't work with z = nil
   end

   context 'when x is nil'
     let(:x) { nil }
     specify { expect(foo(x, y, z)).to eq(15) } 
   end
end

Бонус: subject это магия let

# writing 
subject { foo(x) }
# is almost the same as writing 
let(:subject) { foo(x) }

subject является зарезервированным понятием в RSpec, это "вещь"вы тестируете ", чтобы вы могли написать пример с помощью` foo (x, y, z) следующим образом:

context do 
   let(:x) { 1 }
   let(:y) { 1 }
   let(:z) { 1 }
   subject { foo(x, y, z) }
   specify { expect(subject).to eq(3) }

   context 'when z is nil'
     let(:z) { nil }
     specify { expect(subject).to raise_error) } # foo doesn't work with z = nil
   end

   context 'when x is nil'
     let(:x) { nil }
     specify { expect(foo(subject)).to eq(15) } 
   end
end

Относительно вашей ошибки ...

* 1044Объявления *

let и subject не предназначены для вызова в хуке before(:context), поскольку они существуют для определения состояния, которое сбрасывается между каждым примером, в то время как before(:context) существует для
определения состояния, котороеявляется общим для примеров в группе примеров.

вы делаете что-то вроде

before do
  let(:x) { ... }
end

просто не делайте этого, вы определяете let внутри describe иcontext, но вы можете использовать их (не определять их, использовать то, что определено) внутри before и specify:

let(:name) { 'Frank' }
before do
  User.create name: name
end

specify do
   expect(User.where(name: name).count).to eq(1)
end
...