Итак, у меня есть несколько комментариев ...
Ожидания (expect
операторы) должны быть внутри примера (it
) блока, а не внутри блока before
. В блоке before
можно использовать операторы разрешений (например, allow(ClassX).to receive(:method) { object }
), модификации данных, которые нельзя выполнить в объявлении тестовых переменных, или триггеры запросов. См. http://www.betterspecs.org/ для некоторых примеров. TL; DR, этот общий контекст не является подходящим способом для проверки.
Способ проверки того, что policy_scope вызывается с указанными параметрами c:
# You can put something generic like this in a shared context and then
# define 'params' and 'scoped_result' as let vars in the specs that include
# the shared context
let(:request) { get '/companies' }
let(:params) { user_context or whatever }
let(:scoped_result) { relation }
# By using abstract variable names here, we make this reusable
it 'calls policy scope' do
expect(Pundit).to receive(:policy_scope!).with(params)
request
end
it 'scopes result' do
expect(Pundit.policy_scope!(params)).to eq(scoped_result)
end
Чтобы его смутить и заглушки его ответов, вы должны сделать:
before do
# This ensures Pundit.policy_scope!(context) always returns scoped_result
allow(Pundit).to receive(:policy_scope!).with(context) { scoped_result }
end
... но это очень плохие / хрупкие тесты , особенно если это относится к запросу spe c. Ваши политики Pundit уже должны быть протестированы в файлах политики spe c (см. https://github.com/varvet/pundit#rspec), поэтому то, что вы действительно хотите сделать, - это проверить, что ваша конечная точка возвращает правильный вывод (в области видимости) ответ) с учетом определенного ввода (аутентифицированный управляемый объект политики) . Это плохая идея, пытаться переопределить функциональность Pundit, высмеивая ответ, потому что спецификации вашей конечной точки будут продолжать проходить, если вы внесете критические изменения в код своей политики. Здесь вы хотите настроить тестовые переменные в соответствии с обстоятельствами, которые могут привести к успешному запросу, но убедитесь, что все генерируется c, чтобы его можно было использовать повторно. Для спецификации запроса вы можете сделать что-то вроде:
# Shared context stuff
let(:json) { JSON.parse(response.body) }
let(:headers) { ...define the headers to use across requests...}
before { request }
shared_examples_for 'success' do
it { expect(response).to have_http_status(:success) }
it { expect(json).to eq(expected) } # or something
end
# User spec that includes shared context
include_context 'above stuff'
let(:request) { get '/companies', params: params, headers: headers }
let(:params) { { user_id: user.id } } # or something
let!(:admin_thing) {
...something that should be excluded by the pundit policy used by endpoint...
}
context 'restricted' do
let!(:user) { create :user, :restricted }
let(:expected) { ...stuff scoped to restricted user... }
it_behaves_like 'success'
end
context 'manager' do
let!(:user) { create :user, :manager }
let(:expected) { ...stuff scoped to manager user... }
it_behaves_like 'success'
end
context 'superuser' do
let!(:user) { create :user, :superuser }
let(expected) { ...unscoped stuff visible to super user... }
it_behaves_like 'success'
end
Обратите внимание, что на более высоком уровне (общий контекст) имя и функция являются обобщенными c. На нижнем уровне (spe c объявляет разрешенных пользователей) spe c преобразует абстрактные имена в значения, указывающие c на тестируемую конечную точку. Спецификация c также создает дополнительный объект, который не должен возвращать областью действия политики (по сути, проверяя область видимости, подтверждая, что этот объект исключен из результата). Надеюсь, это поможет.