0

我正在尝试测试控制器以确保只有授权方才能使用 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电话会为我处理这个问题。但显然不是。

我在这里错过了什么,我做错了什么?我怎样才能让这个测试通过?

4

3 回答 3

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)

这将导致current_user.company返回模拟模型@company。到现在为止还挺好。

现在到类方法find_all_plans_for_company。这是我有点困惑的地方。在您的规范中,您将find方法存根Company以返回@companyfor id = 1

但实际上,在你的控制器代码中做这样的事情还不够吗?:

  @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-04T05:31:26.927 回答
1

为什么需要嘲笑?

我的标准测试设置是使用 Database Cleaner 从测试期间创建的任何记录中清除数据库。通过这种方式,测试使用真实的数据库记录运行,因此在每次测试后从测试数据库中删除这些记录。

您可能还想看看 Factory Girl 在测试期间创建模型实例(例如,可以轻松创建 10 条公司记录)。

看:

于 2012-08-04T06:11:37.180 回答
0

我有三个想法可以解决您的问题:

  1. 尝试添加attr_accessible :company_id到计划类。

  2. 因为当您创建 company_id 为 1 的计划时,mock_model 实际上并没有保存到数据库中,所以验证失败,因为它不存在于数据库中。

  3. 确保before_save :default_values在 Plan 类中不会混淆新创建实例的 company_id 属性。

于 2012-08-03T23:22:51.017 回答