[ruby] RSpec let ()을 언제 사용합니까?

인스턴스 변수를 설정하기 위해 before 블록을 사용하는 경향이 있습니다. 그런 다음 예제에서 해당 변수를 사용합니다. 나는 최근에왔다 let(). RSpec 문서에 따르면

… 메모 화 된 헬퍼 메소드를 정의합니다. 값은 동일한 예제에서 여러 호출에 걸쳐 캐시되지만 예제에서는 캐시되지 않습니다.

이전 블록에서 인스턴스 변수를 사용하는 것과 어떻게 다른가요? 또한 언제 let()vs 를 사용해야 before()합니까?



답변

나는 let두 가지 이유로 항상 인스턴스 변수를 선호 합니다.

  • 인스턴스 변수는 참조 될 때 존재합니다. 즉, 인스턴스 변수의 맞춤법을 세게 두드리면 새로운 변수가 만들어지고 초기화 nil되어 미묘한 버그와 오 탐지가 발생할 수 있습니다. let메소드를 작성하기 때문에 NameError철자가 틀렸을 때 얻을 수 있습니다. 또한 사양을 쉽게 리팩토링 할 수 있습니다.
  • before(:each)의 예는 후크에 정의 된 인스턴스 변수를 사용하지 않는 경우에도 후크, 각각의 예를하기 전에 실행됩니다. 일반적으로 큰 문제는 아니지만 인스턴스 변수 설정에 시간이 오래 걸리면 사이클이 낭비됩니다. 로 정의 된 메소드의 let경우 초기화 코드는 예제에서 호출 한 경우에만 실행됩니다.
  • 예제의 참조 구문을 변경하지 않고 예제의 로컬 변수에서 직접 let으로 리팩토링 할 수 있습니다. 인스턴스 변수를 리팩토링하는 경우 예제에서 객체를 참조하는 방법을 변경해야합니다 (예 🙂 @.
  • 이것은 약간 주관적이지만 Mike Lewis가 지적했듯이 스펙을 읽기가 더 쉽다고 생각합니다. 나는 모든 종속 객체를 정의하고 블록을 멋지게 let유지 하는 조직을 좋아합니다 it.

관련 링크는 여기에서 찾을 수 있습니다 : http://www.betterspecs.org/#let


답변

인스턴스 변수 및 사용의 차이 let()즉이 let()있다 으르 평가 . 이는 let()정의 된 메소드가 처음 실행될 때까지 평가되지 않음을 의미합니다 .

의 차이 before와는 letlet()당신에게 ‘계단식’스타일 변수의 그룹을 정의하는 좋은 방법을 제공합니다. 이렇게하면 코드를 단순화하여 사양이 조금 나아 보입니다.


답변

let ()을 사용하기 위해 rspec 테스트에서 인스턴스 변수의 모든 사용을 완전히 대체했습니다. 나는 작은 Rspec 클래스를 가르치기 위해 그것을 사용했던 친구를 위해 간단한 예제를 작성했습니다 : http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html

여기에있는 다른 답변 중 일부에서 알 수 있듯이 let ()은 지연 평가되므로로드 해야하는 것만로드합니다. 사양을 건조시키고 더 읽기 쉽게 만듭니다. 실제로 Rspec let () 코드를 inherited_resource gem 스타일로 컨트롤러에서 사용하도록 포팅했습니다. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html

지연 평가와 함께 ActiveSupport :: Concern 및 모든 사양 / 지원 / 동작과 결합 된 다른 장점은 응용 프로그램에 맞는 고유 한 사양 미니 DSL을 만들 수 있다는 것입니다. Rack 및 RESTful 리소스에 대한 테스트를 위해 작성했습니다.

내가 사용하는 전략은 Factory-everything입니다 (Machinist + Forgery / Faker를 통해). 그러나 before (: each) 블록과 함께 사용하여 전체 예제 그룹 세트에 대한 팩토리를 사전로드하여 스펙을 더 빠르게 실행할 수 있습니다. http://makandra.com/notes/770-taking-advantage -rspec-s-let-in-before-blocks


답변

let 이 게으르게 평가되고 부작용 방법을 넣지 말아야 한다는 것을 명심해야 합니다. 그렇지 않으면 let 에서 before (: each)로 쉽게 변경할 수 없습니다 . let 을 사용할 수 있습니다 ! 대신 할 수 는 각 시나리오 전에 평가 될 수 있도록.


답변

일반적으로 let()더 좋은 구문이며 @name모든 곳에서 기호 를 입력하는 것을 저장합니다 . 그러나 주의를 기울이십시오! 나는 발견 let()도 소개 미묘한 버그 (또는 머리를 긁적 적어도) 변수가 않기 때문에 당신이 그것을 사용하려고 할 때까지 실제로 존재하지 …에게 이야기 기호를 다음을 추가하는 경우 puts(가) 후 let()변수가 올바른지 확인은 사양을 수 있습니다 그러나 puts사양이 없으면 실패합니다.이 미묘함을 발견했습니다.

또한 let()모든 상황에서 캐시되지 않는 것으로 나타났습니다 ! 내 블로그에 작성했습니다 : http://technicaldebt.com/?p=1242

어쩌면 나 일까?


답변

let은 본질적으로 Proc로 기능합니다. 또한 캐시되었습니다.

한 가지 문제는 let.

let(:object) {FactoryGirl.create :object}

expect {
  post :destroy, id: review.id
}.to change(Object, :count).by(-1)

let예상 블록 외부 로 전화해야합니다 . 즉, 당신은 FactoryGirl.create당신의 let 블록에 전화 하고 있습니다. 나는 보통 객체가 지속되는지 확인함으로써 이것을한다.

object.persisted?.should eq true

그렇지 않으면 let블록이 처음 호출 될 때 지연 인스턴스화로 인해 데이터베이스 변경이 실제로 발생합니다.

최신 정보

메모를 추가하기 만하면됩니다. 코드 골프 또는이 경우 rspec 골프를 조심하십시오 .

이 경우 객체가 응답하는 메소드를 호출하면됩니다. 따라서 _.persisted?객체 에서 _ 메소드를 진실로 호출합니다 . 내가하려는 것은 객체를 인스턴스화하는 것입니다. 당신은 빈 전화 할 수 있습니까? 또는 nil? 너무. 요점은 테스트가 아니라 개체를 호출하여 생명을 얻는 것입니다.

그래서 당신은 리팩토링 할 수 없습니다

object.persisted?.should eq true

되려고

object.should be_persisted 

객체가 인스턴스화되지 않았기 때문에 … 게으른. 🙂

업데이트 2

렛을 활용하십시오 ! 이 문제를 완전히 피해야하는 인스턴트 객체 생성 구문 . 그것은 뱅킹되지 않은 let의 게으름의 많은 목적을 물리 칠 것입니다.

또한 경우에 따라 추가 옵션을 제공 할 수 있으므로 실제로는 주제 구문 을 활용하는 것이 좋습니다.

subject(:object) {FactoryGirl.create :object}


답변

Joseph에 대한 참고 사항-데이터베이스 객체를 생성 before(:all)하는 경우 트랜잭션에서 캡처되지 않으며 테스트 데이터베이스에 균열이 생길 가능성이 훨씬 큽니다. before(:each)대신 사용하십시오 .

let과 게으른 평가를 사용해야하는 또 다른 이유는이 매우 복잡한 예제 에서처럼 컨텍스트에서 let을 재정 의하여 복잡한 객체를 가져 와서 개별 조각을 테스트 할 수 있기 때문입니다.

context "foo" do
  let(:params) do
     { :foo => foo,  :bar => "bar" }
  end
  let(:foo) { "foo" }
  it "is set to foo" do
    params[:foo].should eq("foo")
  end
  context "when foo is bar" do
    let(:foo) { "bar" }
    # NOTE we didn't have to redefine params entirely!
    it "is set to bar" do
      params[:foo].should eq("bar")
    end
  end
end