2012-08-03 3 views
0

컨트롤러를 테스트하여 권한있는 사용자 만 RSpec을 사용하여 올바른 하위 객체를 볼 수 있는지 확인하려고합니다. 이 오류가 발생하면 내가 잘못하고있는 것을 파악할 수 없습니다.Model.create를 사용한 RSpec 테스팅 컨트롤러

ActiveRecord::RecordInvalid: Validation failed: Company can't be blank 

나는 Plan 개체와 Company 개체를 가지고 있습니다. 스토어에는 많은 계획이 있습니다 (해충 방제 회사를 생각하십시오). 나는 회사에 대한 계획을 검색 할 수있는 알려진 시나리오가 있음을 테스트하려고합니다 (단 하나만 있다고 가정).

계획은 다음과 같습니다

class Plan < ActiveRecord::Base 
    before_save :default_values 

    # Validation 
    validates :amount, :presence => true 
    validates :company, :presence => true 

    # Plans belong to a particular company. 
    belongs_to :company, :autosave => true  

    scope :find_all_plans_for_company, lambda { 
    |company| where(:company_id => company.id) 
    } 
    # Other code ... 

end 

회사는 다음과 같습니다

class Company < ActiveRecord::Base 
    validates :name, :presence => true 
    validates :phone1, :presence => true 

    validates_format_of :phone1, :phone2, 
         :with => /^[\(\)0-9\- \+\.]{10,20}$/, 
         :message => "Invalid phone number, must be 10 digits. e.g. - 415-555-1212", 
         :allow_blank => true, 
         :allow_nil => true 

    has_many :users 
    has_many :plans 

end 

.. 컨트롤러는이

def index 
    @plans = Plan.find_all_plans_for_company(current_user.company) 

    respond_to do |format| 
     format.html # index.html.erb 
     format.json { render json: @plans } 
    end 
    end 

.. 내 RSpec에 테스트 모습처럼 보인다 이런 (변칙적 인 내용으로 가득차 있다면, 실례지만, 나는 그걸로 주변을 둘러보고 작동시키지 못한다.).

describe PlansController do 

    def valid_attributes 
    { 
     :company_id => 1, 
     :amount => 1000 
    } 
    end 

    describe "GET index" do 
    it "should return the Plans for which this users company has" do 

     @company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212") 
     Company.stub(:find).with(@company.id).and_return(@company) 

     controller.stub_chain(:current_user, :company).and_return(@company) 

     plan = Plan.create! valid_attributes 

     get :index, {} 
     assigns(:plans).should eq([plan]) 
    end 

    # Other tests ... 
    end 

end 

문제는, 내가하려고하면 (또는 내가 해봤 미친 다른 변종의) 나는이 오류를 얻을 :

ActiveRecord::RecordInvalid: Validation failed: Company can't be blank 

나는이 같은 일이 왜 모르겠어요를 나는 Company.stub 전화가 나를 위해 이것을 처리 할 것이라고 생각했다. 그러나 명백하게.

여기에 무엇이 누락되어 있으며 무엇이 잘못 되었습니까? 이 시험을 통과하려면 어떻게해야합니까?

+0

당신이뿐만 아니라 컨트롤러 코드를 게시 할 수 있습니까? –

+0

@shioyama - 컨트롤러가 추가되었습니다. –

+0

죄송합니다. 이제 문제가'get : index, {}'전에 발생 했으므로 컨트롤러 코드는별로 중요하지 않습니다. –

답변

2

이 사양의 레이어를 껍질을 벗기고, 의미가 맞는지 확인하고 (무슨 일이 일어나는지 이해할 수 있도록). 첫째, 무엇을 테스트하고 있습니까?

it "should return the Plans for which this users company has" do 

    ... 

    assigns(:plans).should eq([plan]) 

그래서 당신은 현재 사용자의 회사와 관련된 계획이 @plans에 할당되어 있는지 확인합니다. 우리는 그 밖의 모든 것을 깁거나 모방 할 수 있습니다.

컨트롤러 코드를 보면, 우리는이 :

def index 
    @plans = Plan.find_all_plans_for_company(current_user.company) 

우리가 데이터베이스를 타격하지 않고 모델에 의존하지 않고,이 작업을 받아야합니까?

먼저 우리는 companycurrent_user.company에서 가져오고 싶습니다. 이것은 당신의 사양 코드에서이 두 줄이 할 것입니다 :

@company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212") 
    controller.stub_chain(:current_user, :company).and_return(@company) 

이 모의 모델 @company을 반환 current_user.company의 원인이됩니다. 여태까지는 그런대로 잘됐다.

지금은 클래스 메서드 find_all_plans_for_company입니다. 이것은 내가 약간 혼란스러워하는 곳입니다. 귀하의 스펙에서 Companyfind 메소드를 삽입하여 @companyid = 1으로 반환하십시오.

하지만 실제로는, 당신의 컨트롤러 코드? 당신이이 방법을 한 경우, 테스트에 그냥 계획을 조롱 할 수

@plans = current_user.company.plans 

이 그런 짓을 그냥 충분 아닌 것

@plan = mock_model(Plan) 
    @company = mock_model(Company, :plans => [ @plan ]) 
    controller.stub_chain(:current_user, :company).and_return(@company) 

그런 다음 할당이 작동합니다, 당신은 실제로 모델을 생성하거나 데이터베이스를 칠 필요가 없습니다 : 다음 모의 회사의 plans 협회로 돌아갑니다. 여러분은 모의 회사에게 id 나 다른 속성을 줄 필요조차 없으며 어쨌든 스펙과 관련이 없습니다.

아마도 여기에 뭔가 빠져 있습니다. 알려 주시면 알려주세요.

0

나는이 문제를 해결할 수오고 세 가지 생각을 :

  1. 봅니다 클래스를 계획하는 attr_accessible :company_id를 추가.

  2. company_id가 1 인 계획을 만들 때 mock_model이 실제로 데이터베이스에 저장하지 않기 때문에 데이터베이스에 존재하지 않으므로 검증에 실패합니다.

  3. Plan 클래스의 before_save :default_values이 새로 생성 된 인스턴스의 company_id 속성과 엉망이되지 않도록하십시오.

+0

# 1과 # 3은 효과가 없습니다. 그러나이 줄은 다음과 같이 변경되었습니다. @ company = mock_model (Company, : id => 1, : name => "Test Company", : phone1 => "555-121-1212") : @ company = Company.create (: name => "Test Gym", : phone1 => "555-555-5555") 작동합니다.왜 여기 모의 광고를 사용할 수 없습니까? –

+0

간단히 말해서, 실제'Plan'을 만들고 company_id를 1로 지정했지만 id가 1 인 DB에 실제 회사를 만든 적이 없다면 방금 메모리에 모의 모형을 만들었을 것입니다. 따라서 회사의 존재를 확인하는 데 실패합니다. 회사는 존재하지 않습니다. – radixhound

1

왜 모의해야합니까?

내 표준 테스트 설정은 데이터베이스 클리너를 사용하여 테스트 중에 생성 된 모든 레코드에서 데이터베이스를 지우는 것입니다. 이렇게하면 각 테스트 후에 테스트 데이터베이스에서 결과적으로 삭제되는 실제 데이터베이스 레코드로 테스트가 실행됩니다.

테스트 중에 모델의 인스턴스를 만들 때 Factory Girl을 살펴 보는 것이 좋습니다 (예 : 10 개의 회사 레코드를 쉽게 만들 수 있음).

참조 :

+0

RSpec에서 FactoryGirl도 사용하고 있습니다. 따라서 테스트 작성이 훨씬 간단 해지며 고려해야 할 요소이기도합니다. 테스트 후 삽입 된 모든 행이 테스트 후에 롤백되기 때문에 RSpec과 함께하기가 쉽습니다. – davidrac

+0

데이터베이스와 관련된 실제 레코드 사용하기 컨트롤러 사양을 모델 코드에 밀접하게 연결하고 데이터베이스를 실행하기 때문에 실행하는 데 더 많은 시간이 소요됩니다. 일반적으로 사양이 너무 복잡하지 않으면 모델 클래스/인스턴스에 대한 호출을 모의하거나 스텁하는 것이 좋습니다. –

+0

컨트롤러 테스트에서 모델을 완벽하게 분리한다면 모델 테스트 및 컨트롤러 테스트 외에도 함께 작동하도록 세 번째 테스트 세트가 필요합니다. 시스템이 궁극적으로 전체적으로 작동하기 때문에 확실하게이 접근법의 유일한 이점은 테스트 속도입니다. –

관련 문제