Rails RSpec 测试关联模型的补丁更新(通过网络)

Rails RSpec test for patch update (via web) of associated model

我正在做 Michael Hartl Rails 教程(使用 Rails 4 和 RSpec-Rails 3.3.3)并实现具有管理员权限的用户,这它通过向 User 模型添加一个 admin 布尔属性来实现。相反,我决定使用单独的管理模型(User has_one Admin;Admin belongs_to User),它只存储 user_id;如果用户的 ID 存在于 table 中,那么它就是管理员(考虑到管理员与非管理员的预期比例,我认为从长远来看这会更有效率)。

建议进行用户控制器测试,以确保允许的参数不允许通过 web 编辑 admin 属性(用户是使用 Factory Girl 在数据库中创建的):

patch :update, id: user, user: { password: user[:password], password_confirmation: user[:password_confirmation], admin: '1' }

为了测试我的版本,我尝试了以下 RSpec 测试:

context 'attempt to assign non-admin user as admin via update request via web' do
  it 'will not update admin status' do
    session[:user_id] = user.id # user is logged in as required to make patch request
    expect { patch :update, id: user, user: { name: user.name, email: user.email, admin: { user_id: user.id } } }.to change { Admin.count }.by 0
  end
end

这个测试通过了,但是没有,因为允许的参数被正确地限制了。为了检查反向是否有效,我想测试允许用户模型中的管理参数实际上会对 Admin table.

进行更改

我更新了user_controller:

def user_params
  params.require(:user).permit(:name, :email, :password, :password_confirmation, admin: :user_id)
end

和users_controller_spec(检查管理员计数变化 1):

context 'attempt to assign non-admin user as admin via update web request' do
  it 'will not update admin status' do
    session[:user_id] = user.id
    expect { patch :update, id: user, user: { name: user.name, email: user.email, admin: { user_id: user.id } } }.to change { Admin.count }.by 1
  end
end

出现此错误(文档说当分配给关联的对象类型不正确时会引发此错误):

Failure/Error: expect { patch :update, id: user, user: { name: user.name, email: user.email, admin: { user_id: user.id } } }.to change { Admin.count }.by 1
     ActiveRecord::AssociationTypeMismatch:
       Admin(#70320649341140) expected, got ActionController::Parameters(#70320662053980)

我觉得我的想法可能完全错了:Admin 模型应该只直接更新而不是通过 User 模型,并且可能通过将 Admin 分离到一个像这样的单独模型中给用户管理员身份的恶意补丁请求甚至可能是不可能的(没有 admin_controller 或管理路由,因为我预计这种分配将在数据库级别进行;只有一个简单的管理模型表明其 belongs_to 与用户的关联)。

根据我的情况,我非常感谢您提供一些关于我是否应该编写测试的建议。

提前致谢。

顺便说一句,Michael Hartl 没有自己的支持论坛,and/or SO 上的特定标签吗?

所以我在分支上遇到错误:

1) UsersController attempt to assign non-admin user as admin via update web request will update admin status
     Failure/Error: expect { patch :update, id: user, user: { name: user.name, email: user.email, admin: { user_id: user.id } } }.to change { Admin.count }.by 1
     ActiveRecord::AssociationTypeMismatch:
       Admin(#70235772428120) expected, got ActionController::Parameters(#70235708075860)

一个一般性提示就是不要编写控制器规格...只喜欢功能和单元测试

以下是我对问题的剖析

[tansaku@Samuels-MBP-2:~/Documents/Github/MakersAcademy/Students/April2015/AndyGout/theatrebase ((ac406cd...))]$ 
→ rspec ./spec/controllers/users_controller_spec.rb:89
Run options: include {:locations=>{"./spec/controllers/users_controller_spec.rb"=>[89]}}

UsersController
  attempt to assign non-admin user as admin via update web request

[29, 38] in /Users/tansaku/Documents/Github/MakersAcademy/Students/April2015/AndyGout/theatrebase/app/controllers/users_controller.rb
   29:     @page_title = @user.name
   30:   end
   31: 
   32:   def update
   33:     require 'byebug' ; byebug
=> 34:     if @user.update_attributes(user_params)
   35:       flash[:success] = "Profile updated successfully: #{@user.name}"
   36:       redirect_to @user
   37:     else
   38:       @page_title = User.find(params[:id]).name
(byebug) @user
#<User:0x007fede0b81b60>
(byebug) user_params
{"name"=>"Andy Gout", "email"=>"andygout@example.com", "admin"=>{"user_id"=>"1"}}

我认为这里的问题似乎是您没有设置为通过简单的参数哈希创建新的管理对象

我看到了两种解决方法

  1. 操纵参数以插入新的管理对象
  2. 使用accepts_nested_attributes_for http://guides.rubyonrails.org/form_helpers.html#building-complex-forms

谢谢@SamJoseph!

我也会检查 Hartl 的支持(尽管我意识到我偏离了他的指南......)。

这是帮助我解决问题的第二个建议,使用以下更改:-

用户(用户模型):

accepts_nested_attributes_for :admin

users_controller:

def user_params
  params.require(:user).permit( :name,
                                :email,
                                :password,
                                :password_confirmation,
                                admin_attributes: :user_id
                              )
end

users_controller_规格:

  context 'attempt to assign non-admin user as admin via update web request' do
    it 'will update admin status' do
      session[:user_id] = user.id
      expect { patch :update, id: user, user: { name: user.name, email: user.email, admin_attributes: { user_id: user.id } } }.to change { Admin.count }.by 1
    end
  end

这通过了测试,所以我现在可以自信地从中开始工作,根据需要测试逆。

它非常有用,因为 accepts_nested_attributes_for 是我很快就会再次需要的东西。

我有点不确定你在第一个建议中的意思:操纵参数以插入一个新的管理对象 - 这是否以某种方式在参数中添加命令以直接创建数据库中的新管理员条目(或类似的东西)?我会进一步调查。

我也可以检查重新。不写控制器规范:鉴于这似乎是一个重要的测试,编写一个提出这个直接补丁请求的功能规范会更好吗?你会推荐什么?

再次感谢!