2014-09-10 3 views
6

instance_double을 사용할 때 간헐적 인 테스트 실패가 발생합니다.간헐적 인 spec failure를 생성하는 Rspec의 instance_double

파일에 4 가지 사양이 있습니다. 여기에 소스가 있습니다 :

require 'rails_helper' 

describe SubmitPost do 
    before(:each) do 
    @post = instance_double('Post') 
    allow(@post).to receive(:submitted_at=) 
    end 

    context 'on success' do 
    before(:each) do 
     allow(@post).to receive(:save).and_return(true) 

     @result = SubmitPost.call(post: @post) 
    end 

    it 'should set the submitted_at date' do 
     expect(@post).to have_received(:submitted_at=) 
    end 

    it 'should call save' do 
     expect(@post).to have_received(:save) 
    end 

    it 'should return success' do 
     expect(@result.success?).to eq(true) 
     expect(@result.failure?).to eq(false) 
    end 
    end 

    context 'on failure' do 
    before(:each) do 
     allow(@post).to receive(:save).and_return(false) 

     @result = SubmitPost.call(post: @post) 
    end 

    it 'should return failure' do 
     expect(@result.success?).to eq(false) 
     expect(@result.failure?).to eq(true) 
    end 
    end 

end 

이것은 Rails 4.1.4 어플리케이션입니다. 내부적으로 SubmitPat은 submit_at를 설정하고 전달 된 Post에 save를 호출합니다. 내 게시물 모델은 다음과 같습니다.

class Post < ActiveRecord::Base 

    validates :title, presence: true 
    validates :summary, presence: true 
    validates :url, presence: true 
    validates :submitted_at, presence: true 

    scope :chronological, -> { order('submitted_at desc') } 

end 

슈퍼 바닐라입니다.

rake, rspec 또는 bin/rspec을 실행하면 네 번의 테스트가 모두 20 % - 30 % 실패합니다. 오류 메시지는 항상 :

Failure/Error: allow(@post).to receive(:submitted_at=) 
    Post does not implement: submitted_at= 

내가 focus: true와 사양 중 하나를 레이블 경우

, 하나의 스펙이 시간의 100 % 실패합니다.

instance_doubledouble으로 바꾸면 모든 사양이 100 % 성공합니다.

instance_double은 Post 클래스에서 사용할 수있는 메소드를 추론하는 데 어려움을 겪고있는 것으로 보입니다. 또한 다소 임의적이고 타이밍 기반으로 보입니다.

누구에게이 문제가 발생 했습니까? 어떤 아이디어가 잘못된 것일까 요? 이 문제를 해결하는 방법에 대해 알고 싶습니다. 당연히 디버깅 중단 점을 삽입하면 사양이 100 % 시간을 지납니다.

+0

여기에 대한 데이터가 더 있습니다. 문제의 근본 원인은이 스펙이 실행될 때 Rails 환경이로드되지 않는다는 것입니다. 이 파일 만 실행하면 항상 실패합니다. 총 4 개의 사양 파일이 있습니다. 이 파일이 먼저 실행되면 실패합니다. 다른 것이 먼저 실행되면 성공합니다. 내 가정은 이전 테스트가 Rails 환경을로드 중이므로이 테스트가 통과한다는 것입니다. 이 테스트 파일은'spec/interactors'에 위치하므로 문제가 될 수 있습니다. – EricM

+0

추가 데이터. SubmitPost가있는 app/interactors가 eager_load_paths에 있음을 확인했습니다. 필자는 spec : : feature로 태그를 지정하려고 시도했다. 오류 메시지는 변경되지 않습니다.내가이 문제를 해결 돕는 – EricM

답변

4

ActiveRecord가 동적으로 열 메서드를 만드는 문제가 있습니다. instance_double'Post'을 사용하여 클래스가 아직 존재하지 않거나로드되지 않은 경우 올바르게 찾지 못하도록 메소드를 찾습니다.

이전 사양에서 모델을로드 할 때 ActiveRecord가 이러한 동적 메서드를 만들어 RSpec이 메서드를 찾을 때 (예 : respond_to? 호출) 사양이 전달되도록합니다. 고립 상태에서 실행될 때 모델은 이전에 사용되지 않았으므로 ActiveRecord는 동적 방법을 아직 생성하지 않았으며 경험에 따라 테스트가 실패합니다.

그들이 당신의 사양에 호출 될 때 이에 대한 해결 방법은 동적 인 방법을로드 액티브을 강제하는 것입니다

:

https://www.relishapp.com/rspec/rspec-mocks/docs/verifying-doubles/dynamic-classes :

class Post < ActiveRecord::Base 
    def submitted_at=(value) 
    super 
    end 
end 

문제에 대한 자세한 설명과 해결 방법에 대한 RSpec에 설명서를 참조하십시오

+0

덕분에, 나는에 클래스를 연 '전 (: 컨텍스트) 할 클래스 MyClass에 <액티브 :: 내 사양 및 오버라이드의 자료 데프 my_attribute_setter_or_getter 슈퍼 끝 끝 end' 블록 방법. 그런데 깨달았습니다. MyClass.new와 스텁을 사용할 수있을 때 왜 지구상에이 문제가 생길까요? @EricM 당신은 단지 unpersisted 게시물을 사용하여 번거 로움을 줄일 수 있습니까? '@post = Post.new' – ryan2johnson9

+0

위의 코멘트를 참조하십시오. 나는 왜 double ("MyClass")이 MyClass보다 나은지 알아 냈습니다. 스텁을위한 새로운. 후자는 어떤 것을 스터핑 할 수있게하므로 스펙에서 스터 빙하는 코드에서 메소드의 이름을 변경하면 스펙에서 실패하지 않고 거짓 긍정을 얻게됩니다. instance_double을 사용하면 좋은 오류가 발생합니다. MyClass.new와 같은 효과를 얻으려면 다음과 같은 메소드를 스텁하기 전에 스펙에 라인을 추가해야합니다. expect (my_double) .to respond_to ('the_method_i_am_about_to_stub')' – ryan2johnson9