RSpec의 주제와 let의 차이점은 무엇입니까? 언제 사용해야합니까?


84

http://betterspecs.org/#subjectsubject및 에 대한 정보가 let있습니다. 그러나 나는 그들 사이의 차이점에 대해 여전히 불분명합니다. 또한, SO 포스트 RSpec 테스트에서 before, let 및 subject를 사용하는 것에 대한 주장은 무엇입니까? subject또는을 사용하지 않는 것이 좋습니다 let. 어디로 갈까요? 난 정말 혼란스러워.

답변:


186

요약 : RSpec의 주제는 테스트중인 개체를 참조하는 특수 변수입니다. 한 줄 예제를 지원하는 암시 적으로 기대치를 설정할 수 있습니다. 일부 관용적 인 경우 독자에게 분명하지만 그렇지 않으면 이해하기 어렵고 피해야합니다. RSpec의 let변수는 단지 느리게 인스턴스화 된 (메모리 화 된) 변수입니다. 주제만큼 따르기 어렵지는 않지만 여전히 얽힌 테스트로 이어질 수 있으므로 신중하게 사용해야합니다.

주제

작동 원리

주제는 테스트 대상입니다. RSpec은 주제에 대한 명확한 아이디어를 가지고 있습니다. 정의되거나 정의되지 않을 수 있습니다. 그렇다면 RSpec은 명시 적으로 참조하지 않고 메서드를 호출 할 수 있습니다.

기본적으로 가장 바깥 쪽 예제 그룹 ( describe또는 context블록) 의 첫 번째 인수가 클래스 인 경우 RSpec은 해당 클래스의 인스턴스를 생성하고이를 주제에 할당합니다. 예를 들어, 다음 패스 :

class A
end

describe A do
  it "is instantiated by RSpec" do
    expect(subject).to be_an(A)
  end
end

다음을 사용하여 주제를 직접 정의 할 수 있습니다 subject.

describe "anonymous subject" do
  subject { A.new }
  it "has been instantiated" do
    expect(subject).to be_an(A)
  end
end

주제를 정의 할 때 이름을 지정할 수 있습니다.

describe "named subject" do
  subject(:a) { A.new }
  it "has been instantiated" do
    expect(a).to be_an(A)
  end
end

주제의 이름을 지정하더라도 익명으로 참조 할 수 있습니다.

describe "named subject" do
  subject(:a) { A.new }
  it "has been instantiated" do
    expect(subject).to be_an(A)
  end
end

둘 이상의 명명 된 주제를 정의 할 수 있습니다. 가장 최근에 정의 된 명명 된 주제는 anonymous subject입니다.

그러나 주제는 정의되어 있습니다.

  1. 느리게 인스턴스화됩니다. 즉, 설명 된 클래스의 암시 적 인스턴스화 또는 전달 된 블록의 실행은 예제에서 명명 된 주제가 참조 subject될 때까지 발생하지 않습니다 subject. 설명 주제를 열심히 인스턴스화하려면 (그룹의 예제가 실행되기 전에) subject!대신 라고 말하십시오 subject.

  2. 기대치를 암시 적으로 설정할 수 있습니다 (쓰기 subject또는 명명 된 주제의 이름 없이 ) :

    describe A do
      it { is_expected.to be_an(A) }
    end
    

    제목은이 한 줄 구문을 지원하기 위해 존재합니다.

사용시기

암시 적 subject(예제 그룹에서 추론)은 이해하기 어렵습니다.

  • 뒤에서 인스턴스화됩니다.
  • 암시 적으로 ( is_expected명시 적 수신자없이 호출 하여) 사용하든 명시 적으로 (같이 subject) 사용하든 , 예상이 호출되는 객체의 역할이나 특성에 대한 정보를 독자에게 제공하지 않습니다.
  • 한 줄짜리 예제 구문에는 예제 설명 ( it일반 예제 구문 의 문자열 인수 )이 없으므로 독자가 예제의 목적에 대해 가지고있는 유일한 정보는 예상 자체입니다.

따라서 모든 독자가 컨텍스트를 잘 이해할 수 있고 실제로 예제 설명이 필요하지 않은 경우 암시 적 주제를 사용하는 것이 도움이됩니다 . 표준 사례는 shoulda 매 처로 ActiveRecord 유효성 검사를 테스트하는 것입니다.

describe Article do
  it { is_expected.to validate_presence_of(:title) }
end

익명 subject( subject이름없이 정의 됨 )은 독자가 인스턴스화 방법을 볼 수 있기 때문에 조금 더 좋습니다.

  • 여전히 따라하기 어려운 주제의 인스턴스화를 사용 된 위치에서 멀리 떨어 뜨릴 수 있습니다 (예 :이를 사용하는 많은 예제가있는 예제 그룹의 맨 위).
  • 암시 적 주체가하는 다른 문제가 있습니다.

명명 된 주체는 의도를 드러내는 이름을 제공하지만 let변수 대신 명명 된 주체를 사용하는 유일한 이유 는 익명 주체를 사용하려는 경우이며 익명 주체가 이해하기 어려운 이유를 방금 설명했습니다.

따라서 명시 적 익명 subject또는 명명 된 주체 의 합법적 인 사용 은 매우 드뭅니다 .

let 변수

작동 원리

let 변수는 두 가지 차이점을 제외하고 명명 된 주제와 같습니다.

  • 그들은 정의하고 let/ let!대신 subject/subject!
  • 그들은 익명을 설정하지 않거나 subject암시 적으로 기대할 수 있도록 허용 하지 않습니다 .

사용시기

let예제 간의 중복을 줄이기 위해 사용 하는 것은 완전히 합법적 입니다. 그러나 테스트 명확성을 희생하지 않는 경우에만 그렇게하십시오. 사용하기에 가장 안전한시기 letlet변수의 목적이 이름에서 완전히 명확하고 (따라서 독자가 각 예제를 이해하기 위해 여러 줄이있을 수있는 정의를 찾을 필요가 없도록) 동일한 방식으로 사용되는 경우입니다. 모든 예에서. 그 중 하나라도 사실이 아니라면, 평범한 오래된 지역 변수에 객체를 정의하거나 예제에서 바로 팩토리 메소드를 호출하는 것을 고려하십시오.

let!게으르지 않기 때문에 위험합니다. 누군가가 포함 된 예제 그룹에 예제를 추가 let!했지만 예제에 let!변수 가 필요하지 않은 경우

  • 그 예는 이해하기 어려울 것입니다. 왜냐하면 독자는 let!변수 를보고 그것이 예에 어떤 영향을 미치는지 궁금 할 것이기 때문입니다.
  • let!변수 를 만드는 데 걸리는 시간 때문에 예제는 필요한 것보다 느립니다.

따라서 let!미래의 예제 작성자가 그 함정에 빠질 가능성이 적은 작고 단순한 예제 그룹에서만을 사용하십시오.

예시 당 단일 기대 페티쉬

let별도로 논의 할 가치가있는 주제 또는 변수 의 일반적인 남용이 있습니다. 어떤 사람들은 다음과 같이 사용하고 싶어합니다.

describe 'Calculator' do
  describe '#calculate' do
    subject { Calculator.calculate }
    it { is_expected.to be >= 0 }
    it { is_expected.to be <= 9 }
  end
end

(이것은 우리가 두 가지 기대치를 필요로하는 숫자를 반환하는 메서드의 간단한 예입니다. 그러나 메서드가 많은 기대치를 필요로하는 더 복잡한 값을 반환하고 / 또는 많은 부작용이있는 경우이 스타일은 더 많은 예 / 기대치를 가질 수 있습니다. 모두 기대가 필요합니다.)

사람들은 예제 당 하나의 기대 값 만 가져야한다고 들었 기 때문에 (예제 당 하나의 메서드 호출 만 테스트해야한다는 유효한 규칙과 섞여 있음) 또는 RSpec 까다로운 것을 좋아하기 때문에 이렇게합니다. 익명, 명명 된 주체 또는 let변수로 하지 마십시오 ! 이 스타일에는 몇 가지 문제가 있습니다.

  • 익명의 주제는 예제의 주제가 아닙니다 . 방법 이 주제입니다. 이런 식으로 테스트를 작성하면 언어가 망가져 생각하기가 더 어려워집니다.
  • 항상 한 줄짜리 예처럼 기대의 의미를 설명 할 여지가 없습니다.
  • 주제는 각 예에 대해 구성되어야하며 느립니다.

대신 단일 예를 작성하십시오.

describe 'Calculator' do
  describe '#calculate' do
    it "returns a single-digit number" do
      result = Calculator.calculate
      expect(result).to be >= 0
      expect(result).to be <= 9
    end
  end
end

17
와! + 💯! "예제 당 단일 기대 페티쉬"는 말하자면 금으로도 그만한 가치가 있습니다. 나는 그 (잘못된) 규칙을 매우 밀접하게 따를 필요가 없다고 느낀 적이 없었지만 이제는 우리에게 그것을 강요하려는 사람들에 대해 잘 무장하고 있습니다. 감사!
우상 파괴자

2
또한 다중 기대 블록이 모든 기대 행을 실행하도록하려면 (첫 번째 실패시 실행되지 않는 대신) 다음 :aggregate_failures과 같은 행 에서 태그를 사용할 수 있습니다 it ​"marks a task complete"​, ​:aggregate_failures​ ​do(Rails 5 Test Prescriptions 책에서 가져옴)
labyrinth

1
나는 모두 과대 광고와 페티쉬에 반대합니다. "예제 당 단일 기대"는 주로 하나의 예에서 관련없는 많은 기대를 그룹화하는 것을 좋아하는 사람들을위한 것입니다. 의미 론적으로 말해서, 당신의 예는 이상적이지 않습니다. 한 자리 숫자에 대한 유효성을 검사하지 않고 [0, 9]의 실수에 대한 유효성을 검사합니다. 놀랍게도 훨씬 더 읽기 쉬운 단일 기대 값으로 코딩 할 수 있습니다 expect(result).to be_between(0, 9).
Andre Figueiredo

한 자리 숫자 (Int [0,9])에 대해서만 유효성을 검사하려면 더 적합 할 것입니다. expect(result.to_s).to match(/^[0-9]$/)추악하다는 것을 알고 있지만 실제로 말하는 내용을 테스트하거나 between+를 사용 is_a? Integer하지만 여기서는 테스트 중입니다. 유형도. 그리고 단지 let.. 그것은 관심 대상이되어서는 안되며, 실제로는 예제 사이의 값을 재평가하는 것이 더 나을 수 있습니다. 그렇지 않으면 게시물 +1
Andre Figueiredo

나는 위험에 대한 당신의 설명을 좋아 let!하고 내 팀원을 스스로 설득하지 못했습니다. 이 답변을 보내 드리겠습니다.
Damon Aw

5

Subject그리고 let당신의 검사 결과까지 당신이 정돈 도움과 속도 단지 도구입니다. rspec 커뮤니티의 사람들은 그것들을 사용하기 때문에 그것들을 사용해도 괜찮은지는 걱정하지 않을 것입니다. 유사하게 사용할 수 있지만 용도가 약간 다릅니다.

Subject테스트 주제를 선언 한 다음 나중에 다음 테스트 케이스에 재사용 할 수 있습니다. 이렇게하면 코드 반복이 줄어 듭니다 (코드 건조).

Letbefore: each테스트 데이터를 인스턴스 변수에 할당 하는 블록 의 대안 입니다. Let몇 가지 장점이 있습니다. 첫째, 인스턴스 변수에 할당하지 않고 값을 캐시합니다. 둘째, 게으르게 평가됩니다. 즉, 사양에서 요청할 때까지 평가되지 않습니다. 따라서 let테스트 속도를 높일 수 있습니다. let읽기도 쉽다고 생각합니다


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