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


90

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

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

아래 예에서 좋은 접근 방식 (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전달 했지만 콧수염이없는 경우 의 반환 값을 확인하면 다음 person과 top_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.