Тест Rails RSpec для обновления патча (через Интернет) связанной модели

Я выполняю учебник Майкла Хартла по Rails (используя Rails 4 и RSpec-Rails 3.3.3) и реализую пользователей с правами администратора, что достигается путем добавления логического атрибута администратора в модель пользователя. Вместо этого я решил использовать двойную модель администратора (у пользователя есть_один администратор; администратор принадлежит пользователю), в которой просто хранится user_id; если идентификатор пользователя существует в таблице, то это администратор (я чувствую, что это будет более эффективным в долгосрочной перспективе, учитывая ожидаемое соотношение администраторов и неадминистраторов).

Предлагается тест пользовательского контроллера, чтобы убедиться, что разрешенные параметры не позволяют редактировать атрибут администратора через Интернет, например, так (пользователь создается в базе данных с помощью 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

Этот тест проходит успешно, но не благодаря правильному ограничению разрешенных параметров. Чтобы проверить, работает ли обратное, я хочу проверить, что разрешение параметров администратора в модели пользователя фактически внесет изменения в таблицу администратора.

Я обновил 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_controller или маршрутов администратора, поскольку я ожидаю, что такое назначение будет выполнено на уровне базы данных; только простая модель администратора, в которой указано, что она принадлежит пользователю).

Я был бы очень признателен за совет о том, есть ли тест, который я должен написать, учитывая мои обстоятельства.

Заранее спасибо.


person Andy Gout    schedule 08.10.2015    source источник
comment
привет @andy-gout - есть ли шанс, что у нас может быть ссылка на ваш репозиторий github для этого?   -  person Sam Joseph    schedule 09.10.2015
comment
пожалуйста, отправьте эти обновления в ветку и дайте ссылку на эту ветку, если это возможно   -  person Sam Joseph    schedule 09.10.2015


Ответы (2)


Кстати, у Майкла Хартла нет собственного форума поддержки и/или определенного тега на 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"=>"[email protected]", "admin"=>{"user_id"=>"1"}}

Я думаю, проблема здесь в том, что вы не настроены для создания нового объекта администратора с помощью простого хэша параметров.

Я вижу два пути вокруг этого

  1. манипулировать параметрами, чтобы вставить новый объект администратора
  2. используйте accepts_nested_attributes_for http://guides.rubyonrails.org/form_helpers.html#building-complex-forms
person Sam Joseph    schedule 13.10.2015

Спасибо @SamJoseph!

Я также проверю поддержку Хартла (хотя я осознаю, что отклоняюсь от его руководства...).

Это было второе предложение, которое помогло мне найти решение, используя следующие изменения: -

пользователь (модель пользователя):

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_spec:

  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 мне скоро снова понадобится.

Я был немного не уверен в том, что вы имели в виду в первом предложении: манипулировать параметрами, чтобы вставить новый объект администратора - это каким-то образом добавить команду в параметры для непосредственного создания новой записи администратора в базе данных ( Или что-то в этом роде)? Я буду исследовать дальше.

Могу ли я также проверить повторно. не писать спецификации контроллера: учитывая, что это важный тест, не лучше ли написать спецификацию функций, которая делает этот прямой запрос на исправление? Чтобы вы посоветовали?

Спасибо еще раз!

person Andy Gout    schedule 14.10.2015
comment
что касается не написания спецификаций контроллера, я думаю, что есть место для написания подобных тестов, которые проверяют безопасность приложения. Но обратите внимание, что здесь вы просто тестируете функциональность, которую вы также хотите сделать доступной через веб-тестирование на основе функций, поэтому это избыточно, если у вас нет строгих требований к не веб-API в вашем приложении. Если вы вернете тест к проверке безопасности исходной конечной точки API, все в порядке. Извините, это своего рода побочный вопрос. Меня немного смутило то, как ваша тестовая активность отличается от типа теста, который вы используете только для изучения :-) - person Sam Joseph; 15.10.2015