我有一个看起来像这样的测试:
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,我相信我正在测试中,但显然,这不起作用。
答案 0 :(得分:3)
您没有创建current_user
。它仅在test
块中初始化。
有两种不同的方法可以做到:
首先,使用设计测试助手。像这样的东西
let(:curr_user) { FactoryGirl.create(:user, ...attrs...) }
sign_in curr_user
其次,您可以在控制器中存储current_user
方法以进行测试env
controller.stub(current_user: FactroryGirl.create(:user, ...attrs...))
您应该使用FactoryGirld.create(...)
而不是FactoryGirl.build(...)
,因为必须保留工厂对象。(保存在db中并且id
属性不是nil)
答案 1 :(得分:1)
有几件事情浮现在脑海中:
FactoryGirl.build(:user, ...)
返回未保存的用户实例。我建议使用Factory.create
代替它,因为对于未保存的实例,没有id
,并且没有办法(通常基于会话的)current_user
getter从数据库加载它。如果您正在使用Devise,则应在创建后“登录”用户。这包括在DB中保存记录并将其引用到会话中。 See devise wiki
此外,将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参考约束,否则在操作触发之前,产品实例可能需要存在于DB中。