RSpec : let과 before 블록의 차이점은 무엇입니까?


90

RSpec에서 letbefore블록의 차이점은 무엇입니까 ?

그리고 언제 각각을 사용합니까?

아래 예에서 좋은 접근 방식 (let 또는 before)은 무엇입니까?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

stackoverflow 게시물을 연구했습니다.

그러나 위와 같은 연관성에 대해 let을 정의하는 것이 좋은가요?


1
기본적으로 'let'은 인스턴스 변수를 싫어하는 사람들이 사용합니다. 참고로 FactoryGirl 또는 유사한 도구 사용을 고려해야합니다.
apneadiving

답변:


147

사람들은 서로 다른 몇 가지 기본적인 방법을 before(:all)설명 했지만 생략했고 왜 사용해야하는지 정확히 설명하지 않았습니다.

인스턴스 변수는 부분적 으로이 답변에 언급 된 이유 때문에 대부분의 사양에서 사용되는 장소가 없다고 생각 하므로 여기에서 옵션으로 언급하지 않겠습니다.

블록하자

let블록 내의 코드 는 참조 될 때만 실행되며 지연로드는 이러한 블록의 순서가 무관하다는 것을 의미합니다. 이렇게하면 사양을 통해 반복되는 설정을 줄일 수있는 많은 전력이 제공됩니다.

이에 대한 한 가지 (매우 작고 인위적인) 예는 다음과 같습니다.

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

has_moustache각 경우에 다르게 정의되어 있음을 알 수 있지만 subject정의 를 반복 할 필요는 없습니다 . 주목할 점은 let현재 컨텍스트에 정의 된 마지막 블록이 사용된다는 것입니다. 이는 대부분의 사양에 사용할 기본값을 설정하는 데 유용하며 필요한 경우 덮어 쓸 수 있습니다.

예를 들어, true 로 설정된 모델을 calculate_awesome전달 했지만 콧수염이없는 경우 의 반환 값을 확인하면 다음 persontop_hat같습니다.

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

let 블록에 대해주의해야 할 또 다른 사항은 데이터베이스에 저장된 무언가를 검색하는 경우 (예 :) Library.find_awesome_people(search_criteria)이미 참조되지 않은 경우 데이터베이스에 저장되지 않으므로 사용해서는 안됩니다. let!또는 before블록은 여기서 사용해야합니다.

또한 블록 실행을 트리거 하는 데 절대 사용 하지 마십시오 .beforeletlet!

허락하다! 블록

let!블록은 정의 된 순서대로 실행됩니다 (이전 블록과 매우 유사). before 블록과의 한 가지 핵심 차이점은 인스턴스 변수로 폴백 할 필요없이이 변수에 대한 명시 적 참조를 얻는 것입니다.

let블록 과 마찬가지로 여러 let!블록이 동일한 이름으로 정의 된 경우 가장 최근 블록이 실행에 사용됩니다. 핵심 차이점은 이렇게 사용하면 let!블록이 여러 번 실행되는 반면 let블록은 마지막 시간에만 실행된다는 것입니다.

before (: each) 블록

before(:each)기본 before 블록이므로 매번 before {}전체를 지정하는 대신 참조 할 수 있습니다 before(:each) {}.

before몇 가지 핵심 상황에서 블록 을 사용하는 것이 개인적으로 선호 합니다. 다음과 같은 경우 before 블록을 사용합니다.

  • 조롱, 스터 빙 또는 복식을 사용하고 있습니다.
  • 합리적인 크기의 설정이 있습니다 (일반적으로 이것은 공장 특성이 올바르게 설정되지 않았다는 신호입니다).
  • 직접 참조 할 필요는 없지만 설정에 필요한 변수가 많이 있습니다.
  • 기능 컨트롤러 테스트를 레일에 작성 중이며 테스트를위한 특정 요청을 실행하고 싶습니다 (예 :) before { get :index }. subject많은 경우에 이것을 사용할 수 있지만 참조가 필요하지 않으면 때때로 더 명확하게 느껴집니다.

before사양에 대한 큰 블록을 작성하는 경우 공장을 확인하고 특성과 유연성을 완전히 이해했는지 확인하십시오.

before (: all) 블록

이들은 현재 컨텍스트 (및 하위)의 사양 이전에 한 번만 실행됩니다. 이는 실행과 노력을 줄일 수있는 특정 상황이 있기 때문에 올바르게 작성하면 큰 이점을 얻을 수 있습니다.

한 가지 예 (실행 시간에 거의 영향을주지 않음)는 테스트를 위해 ENV 변수를 조롱하는 것입니다.이 작업은 한 번만 수행하면됩니다.

도움이되기를 바랍니다 :)


내가있는 미니 테스트 사용자의 경우이 Q & A의 RSpec 태그를 알아 차리지 못한 경우 before(:all)옵션이 미니 테스트에 존재하지 않습니다. 댓글에 대한 몇 가지 해결 방법은 다음과 같습니다. github.com/seattlerb/minitest/issues/61
MrYoshiji

참조 기사는 죽은 링크입니다 : \
딜런 피어스

감사합니다 @DylanPierce. 나는 그 기사의 사본을 찾을 수 없으므로 대신 이것을 해결하는 SO 답변을 참조했습니다. :)
Jay

감사합니다 :) 많은 감사
딜런 피어스에게

1
its더 이상 rspec-core에 없습니다. 보다 현대적이고 관용적 인 방법은 it { is_expected.to be_awesome }.
Zubin

26

거의 항상 let. 링크하는 게시물 let도 더 빠릅니다. 그러나 때로는 많은 명령을 실행 before(:each)해야 할 때 많은 명령이 포함되면 구문이 더 명확 하기 때문에 사용할 수 있습니다 .

귀하의 예제에서, 나는 확실히 사용하는 것을 선호 let대신 before(:each). 일반적으로 변수 초기화가 완료되면 let.


15

언급되지 않은 큰 차이점은로 정의 된 변수 let는 처음 호출 할 때까지 인스턴스화되지 않는다는 것입니다. 따라서 before(:each)블록이 모든 변수를 인스턴스화 하는 동안 let여러 테스트에서 사용할 수있는 여러 변수를 정의 해 보겠습니다. 자동으로 인스턴스화하지는 않습니다. 이를 알지 못하면 모든 데이터가 미리로드 될 것으로 예상하는 경우 테스트가 다시 돌아올 수 있습니다. 경우에 따라 여러 let변수 를 정의한 다음 before(:each)블록을 사용하여 각 let인스턴스 를 호출 하여 데이터를 시작할 수 있는지 확인합니다.


20
let!각 예제 전에 호출되는 메서드를 정의 하는 데 사용할 수 있습니다 . RSpec 문서를 참조하십시오 .
Jim Stewart

3

Machinist를 사용중인 것 같습니다. make에 몇 가지 문제가있을 수 있습니다. 내부 렛 (비뱅 버전)은 글로벌 픽스처 트랜잭션 외부에서 발생하므로 (트랜잭션 픽스처도 사용하는 경우) 다른 테스트의 데이터를 손상시킵니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.