我正在尝试测试一个控制器,以确保只有授权方可以使用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调用会为我处理这个问题。但显然不是。
我在这里遗漏了什么,我做错了什么?我怎样才能让这个测试通过呢?
发布于 2012-08-04 13:31:27
让我们剥离这个规范的层次,以确保它有意义(并确保我理解正在发生的事情)。首先,你在测试什么?
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)我们需要什么才能让它在不影响数据库和不依赖于模型的情况下工作?
首先,我们想从current_user.company中获得一个模拟的company。规范代码中的这两行代码的作用如下:
@company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212")
controller.stub_chain(:current_user, :company).and_return(@company)这将导致current_user.company返回模拟模型@company。到目前一切尚好。
现在来看类方法find_all_plans_for_company。这就是我有点困惑的地方。在您的规范中,您将存根Company上的find方法,以便为id = 1返回@company。
但实际上,仅仅在控制器代码中做这样的事情还不够吗?:
@plans = current_user.company.plans如果你是这样做的,那么在你的测试中,你只需要模拟一个计划,然后将它作为模拟公司的plans关联返回:
@plan = mock_model(Plan)
@company = mock_model(Company, :plans => [ @plan ])
controller.stub_chain(:current_user, :company).and_return(@company)然后赋值应该可以工作,并且您不需要实际创建任何模型或访问数据库。您甚至不需要为您的模拟公司提供id或任何其他属性,这些属性无论如何都与规范无关。
也许我在这里遗漏了什么,如果是这样,请让我知道。
发布于 2012-08-04 14:11:37
你为什么需要嘲笑?
我的标准测试设置是使用Database Cleaner,它从测试期间创建的任何记录中清除数据库。以这种方式,测试使用真实数据库记录运行,这些记录随后在每次测试后从测试数据库中删除。
您可能还想看看Factory Girl在测试期间创建模型实例的过程(例如,它可以轻松地创建10个公司记录)。
请参见:
发布于 2012-08-04 07:22:51
我有三个想法可以解决你的问题:
attr_accessible :company_id添加到计划类。before_save :default_values中不会影响新创建的实例的company_id属性。https://stackoverflow.com/questions/11804448
复制相似问题