Как лучше всего написать пользовательский сопоставитель RSpec для тестирования контроля доступа в приложении Rails

#ruby-on-rails #testing #rspec

Вопрос:

Итак, вместо того, чтобы писать целую кучу спецификаций контроля доступа и дублировать их во многих моих файлах спецификаций, я хочу создать пользовательский сопоставитель. Так что вместо этого:

 describe "access control" do
  it "should prevent access by non-logged-in users"
  it "should prevent access by normal users"
  it "should prevent access by editor users"
  it "should prevent access by admin users"
  it "should allow access by super admin users"
end
 

Я хочу сделать что-то вроде этого:

 lambda do
  get :index
end.should have_access_control(:allowed => [:super_admin], :disallowed => [:admin, :editor, :user])
 

Есть ли какие-либо примеры или предложения о том, как я могу сделать что-то подобное?

Ответ №1:

Хорошо, я нашел метод достижения этой цели, хотя он не использует пользовательский сопоставитель. Включите следующий код в свой файл spec_helper.rb:

 def access_control (code, options={})
  options = {:allow => [], :disallow => []}.merge(options)

  options[:allow].each do |user|
    it "#{code} should allow #{user.to_s}" do
      login_as(user)
      eval code
      response.should_not redirect_to(login_path)
    end
  end

  options[:disallow].each do |user|
    it "#{code} should disallow #{user.to_s}" do
      login_as(user)
      eval code
      response.should redirect_to(login_path)
    end
  end
end
 

И назовем это следующим образом:

 access_control("get :index", {:allow => [:super_admin], :disallow => [:quentin, :admin]})
 

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

Ответ №2:

Я не согласен с вашим решением. Тесты не должны быть областью, в которой можно исключить подобное дублирование. Это затрудняет чтение тестов и их обслуживание. Кроме того, безусловно, есть аргумент в пользу того, что дублирование в тестах может повлиять на ваш дизайн.

В первоначальном примере, который вы перечислили, есть 5 различных контекстов, каждый из которых должен быть протестирован изолированно и понятен с первого взгляда. Ваше решение, хотя и аккуратное, наносит вред этим двум целям.

Комментарии:

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

2. Кроме того, вызов функции гораздо более лаконичен (вместо того, чтобы заполнять спецификацию шестью строками на роль за действие).