1

我有一个看起来像这样的测试:

test "should get create" do
   current_user = FactoryGirl.build(:user, email: 'not_saved_email@example.com')
   assert_difference('Inquiry.count') do
     post :create, FactoryGirl.build(:inquiry)
    end
    assert_not_nil assigns(:inquiry)
    assert_response :redirect
end

这是测试控制器的这一部分:

def create
    @inquiry = Inquiry.new(params[:inquiry])
    @inquiry.user_id = current_user.id
    if @inquiry.save
      flash[:success] = "Inquiry Saved"
      redirect_to root_path
    else
      render 'new'
    end
  end

和工厂:

FactoryGirl.define do

  factory :inquiry do
    product_id 2
    description 'I have a question about....'
  end
end

但我在测试中不断出错:

      1) Error:
test_should_get_create(InquiriesControllerTest):
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id

我究竟做错了什么?我需要设置 current_user,我相信我正在测试中,但显然,这不起作用。

4

2 回答 2

3

你没有创建current_user. 它仅在test块中初始化。有两种不同的方法来做到这一点:

首先,使用设计测试助手。类似的东西

let(:curr_user) { FactoryGirl.create(:user, ...attrs...) }
sign_in curr_user

设计文档

其次,您可以current_user在控制器中为测试环境存根方法

controller.stub(current_user: FactroryGirl.create(:user, ...attrs...))

你应该使用FactoryGirld.create(...)而不是FactoryGirl.build(...),因为你的工厂对象必须被持久化。(保存在数据库中并且id属性不为零)

于 2012-08-20T15:39:32.513 回答
1

有几件事浮现在脑海:

FactoryGirl.build(:user, ...)返回未保存的用户实例。我建议使用它来Factory.create代替它,因为对于未保存的实例,没有id并且(通常基于会话的)current_usergetter 无法从数据库中加载它。如果您使用的是 Devise,您应该在创建后“登录”用户。这包括在数据库中保存记录并将对它的引用放入会话中。见设计维基

此外,将 ActiveRecord 对象传递给create这样的操作对我来说看起来很奇怪:

post :create, FactoryGirl.build(:inquiry)

也许有一些轨道魔法可以识别你的意图,但我建议明确地这样做:

post :create, :inquiry => FactoryGirl.build(:inquiry).attributes

或者更好的是,将其与工厂解耦(测试代码中的 DRY 和美学原则与应用程序代码不同):

post :create, :inquiry => {product_id: '2', description: 'I have a question about....'}

这引用了 id = 2 的产品,除非您的数据库没有 FK 引用约束,否则产品实例可能需要在操作触发之前存在于数据库中。

于 2012-08-20T15:24:37.803 回答