Подтвердить что ты не робот

Rails - RSpec - Разница между "let" и "let!"

Я прочитал, что RSpec manual говорит о различии, но некоторые вещи все еще запутывают. Каждый другой источник, в том числе "Книга RSpec", объясняет только "let", а "The Rails 3 Way" так же запутан, как и руководство.

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

После этого, так как "let!" оценивает, когда определено, и снова при вызове, если тест не сбой, поскольку "count.should eq (1)" должен быть вместо "count.should eq (2)"?

Любая помощь будет оценена.

4b9b3361

Ответ 1

Он не вызывается при определении, а скорее перед каждым примером (а затем он запоминается и не вызывается снова на примере). Таким образом, счетчик будет иметь значение 1.

В любом случае, если у вас есть другой пример, перед вызовом снова вызывается: все следующие тесты проходят:

$count = 0
describe "let!" do
  invocation_order = []

  let!(:count) do
    invocation_order << :let!
    $count += 1
  end

  it "calls the helper method in a before hook" do
    invocation_order << :example
    invocation_order.should == [:let!, :example]
    count.should eq(1)
  end

  it "calls the helper method again" do
    count.should eq(2)
  end
end

Ответ 2

Я понял разницу между let и let! с помощью очень простого примера. Позвольте мне сначала прочитать предложение doc, а затем показать выходные руки.

Опустить:

... let оценивается с лени: он не оценивается до первого раза вызывается метод, который он определяет.

Я понял разницу с приведенным ниже примером: -

$count = 0
describe "let" do
  let(:count) { $count += 1 }

  it "returns 1" do
    expect($count).to eq(1)
  end
end

Позволяет запустить его сейчас: -

[email protected]:~/Ruby> rspec spec/test_spec.rb
F

Failures:

  1) let is not cached across examples
     Failure/Error: expect($count).to eq(1)

       expected: 1
            got: 0

       (compared using ==)
     # ./spec/test_spec.rb:8:in `block (2 levels) in <top (required)>'

Finished in 0.00138 seconds (files took 0.13618 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/test_spec.rb:7 # let is not cached across examples
[email protected]:~/Ruby>

Почему ERROR? Поскольку, как сказал doc, с let, он не оценивается до тех пор, пока не будет вызван первый метод, который он определяет. В этом примере мы не вызывали count, таким образом $count все еще 0, а не увеличивается на 1.

Теперь приступим к части let!. Док говорит, что

.... Вы можете использовать let! для принудительного вызова метода перед каждым примером. Это означает, что даже если вы не вызывали вспомогательный метод в этом примере, все равно он будет вызываться до запуска вашего примера.

Давайте также проверим это: -

Вот модифицированный код

$count = 0
describe "let" do
  let!(:count) { $count += 1 }

  it "returns 1" do
    expect($count).to eq(1)
  end
end

Позволяет запустить этот код: -

[email protected]:~/Ruby> rspec spec/test_spec.rb
.

Finished in 0.00145 seconds (files took 0.13458 seconds to load)
1 example, 0 failures

Смотрите, теперь $count возвращает 1, поэтому тест прошел. Это произошло, когда я использовал let!, который запускался до запуска примера, хотя мы не вызывали count в нашем примере.

Вот как let и let! отличаются друг от друга.

Ответ 3

Вы можете прочитать об этом здесь, но в основном. (:let) оценивается лениво и никогда не будет инстанцироваться, если вы его не назовете, а (:let!) подвергается сильной оценке перед каждым вызовом метода.

Ответ 4

Я также думал, что это запутывает, но я думаю, что примеры из The Rails 3 Way хороши.
пусть аналогично переменным экземпляра в переднем блоке, тогда как пусть! немедленно замечено

От пути Rails 3

describe BlogPost do
  let(:blog_post) { BlogPost.create :title => 'Hello' }
  let!(:comment) { blog_post.comments.create :text => 'first post' }

  describe "#comment" do
    before do
     blog_post.comment("finally got a first post")
    end

    it "adds the comment" do
      blog_post.comments.count.should == 2
    end
  end
end

"Поскольку блок комментариев никогда не был бы выполнен для первого если вы использовали определение let, только один комментарий имел бы был добавлен в эту спецификацию, хотя реализация может работать. Используя let! мы гарантируем, что исходный комментарий будет создан, а спецификация теперь пройдет".

Ответ 5

Я тоже был смущен let и let!, поэтому я взял код документации из здесь и сыграл с ним: https://gist.github.com/3489451

Надеюсь, что это поможет!