2012-02-21 88 views
2

我对Rspec很陌生,正在将代码库从Rails 3.0.x迁移到Rails 3.1.x并同时添加测试。我能够获得基本的控制器测试工作,但在开始整合Devise后,我开始遇到问题。具体来说,我有一个名为User的Devise对象,属于Company,而Company有很多Communities。我试图为Communities控制器编写测试,当尝试通过关联引用(例如controller.current_user.communities)时,我无法引用我创建的Factory对象。当我尝试测试,我得到以下(或类似)错误:Rspec + Devise +关联工厂女孩测试

No route matches {:id=>nil, :controller=>"communities", :action=>"edit"}

我敢肯定,我失去了一些东西基本涉及到Rspec的/ Factory_Girl,但任何帮助,将不胜感激。


设置用于测试edit行动Communities的一个例子是如下:

/config/routes.rb

Units::Application.routes.draw do 

    devise_for :users 

    resources :companies 
    resources :communities 

    ... 

    root :to => 'companies#index' 

end 

/app/models/user.rb

class User < ActiveRecord::Base 
    belongs_to :company 
    has_many :communities, :through => :company 
    ... 
end 

/app/models/company.rb

class Company < ActiveRecord::Base 
    has_many :users 
    has_many :communities 
    ... 
end 

/app/models/community.rb

class Community < ActiveRecord::Base 
    belongs_to :company 
    ... 
end 

/spec/controllers/communities_controller_spec.rb

require 'spec_helper' 

describe CommunitiesController do 
    render_views 
    login_user 
    ... 
    context "GET #edit" do 
    before(:each) do 
     @company = controller.current_user.company 
     @community = controller.current_user.communities.new[Factory.build(:community, :company => @company)] 
     controller.current_user.company.communities.should_receive(:find).with(:company => @company) 
    end 

    it "should be successful" do 
     get :edit, :id => @community 
     response.should be_successful 
    end 

    it "should find a specific community" do 
     get :edit, :id => @community 
     assigns(:community).should eq(@community) 
    end 
    end 
    ... 
end 

/app/controllers/communities_controller.rb

class CommunitiesController < ApplicationController 
    ... 
    def edit 
    @community = current_user.communities.find(params[:id]) 
    end 
    ... 
end 

/spec/support/controller_macros.rb

module ControllerMacros 
    def login_user 
    before(:each) do 
     @request.env["devise.mapping"] = Devise.mappings[:user] 
     company = Factory.create(:company) 
     user = Factory.create(:user, :company => company) 
     # user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module 
     sign_in user 
    end 
    end 
end 
+1

请添加你的'config/routes.rb' – lucapette 2012-02-23 16:48:32

+1

你可以在'it'中添加'p @ community'应该是成功的''部分并给我们结果? – farnoy 2012-02-24 21:30:37

+0

@farnoy我在该部分放了'p @ community',但它没有改变输出。我仍然收到以下错误: '失败/错误:get:edit,:id => @community ActionController :: RoutingError: 没有路由匹配{:id => nil,:controller =>“communities” ,:action =>“edit”}' – theandym 2012-02-26 03:03:11

回答

3

的错误: “无路由匹配{:ID =>零,:控制器=>” 社区 “:动作=> ”编辑“}” 在你前行(的结果:每)块的内容如下:

@community = controller.current_user.communities.new[Factory.build(:community, :company => @company)] 

请注意,Factory.build与Factory.create不同。构建会创建该对象的新实例,但实际上并未将其保存到数据库中。此外,您正在使用这个新的Factory实例创建一个新的社区实例,而不保存它。我建议创建社区如下:

@community = Factory.create(:community, :company => @company) 

你以后采取这一@community变量,并使其在测试的“get”方法:

get :edit, :id => @community 

因为社区不是保存,它没有ID。它只存在于内存中。此外,发送获取@community对象的id(更新Factory.build以读取Factory之后)会更加正确。创建):

get :edit, :id => @community.id 

其次,贵会不会出现与上线在你的控制器的逻辑是一致的:

controller.current_user.company.communities.should_receive(:find).with(:company => @company) 

在你的控制器,你是通过CURRENT_USER链接找到社区。社区,而不是current_user.company.communities。我建议删除这个测试,因为你想测试结果,而不是实现。这使测试过于脆弱,无法编码更改。

最后,我想提一下,您在测试中有多个断言。您声称在您的面前(:每个):

controller.current_user.company.communities.should_receive(:find).with(:company => @company) 

这在各个它下面的嵌套的测试中运行,使每一个测试检查两个条件。这应该更好地隔离。我建议移动此代码来自己的试块,并移出的前法:

it "should find communities" do 
    controller.current_user.company.communities.should_receive(:find).with(:company => @company) 
end 

然而,正如我上面提到的,我不认为这个测试是一个好主意,因为它太紧密结合你正在测试的内容的实现。

+0

非常感谢您的明确解释 - 它非常有意义。此外,避免测试执行而不是输出也是一个好主意;我已经相应地更改了我的代码。 – theandym 2012-02-27 05:51:27