Mantendo -se seco ao testar um controlador, autorizado via Cancan
-
27-09-2019 - |
Pergunta
Estou escrevendo retroativamente alguns testes, usando o RSPEC, para um projeto Rails.
Estou usando a jóia de Cancan para fornecer autorização. Eu decidi escrever uma especificação que testará o ability.rb
modelo. Depois, testei meus modelos restantes.
Mudei para os controladores e encontrei um enorme problema: estou testando minhas habilidades de novo!
Basicamente, tenho que arrancar uma série de modelos e extrair suas associações; Caso contrário, a resposta apenas retorna 403 Forbidden
.
A razão para isso é que o controlador é basicamente responsável por se preocupar com a autorização.
Não sei ao certo para onde ir daqui. Estou saindo de até 6 modelos, apenas para escrever um único teste. EU conhecer as habilidades funcionam, é isso que ability_spec.rb
é para.
Portanto, esta pergunta é realmente 2 vezes:
- Devo estar testando o modelo de habilidade separadamente?
- Os testes do controlador devem se preocupar com as permissões adequadas?
EditarExigir 'spec_helper' incluir o Devise :: testhelpers # para dar seu acesso à sua especificação aos ajudantes
describe TokensController do
before(:each) do
@mock_user = User.new(:username => "bob", :email => "user@user.com", :password => "longpassword")
@mock_user.role = "admin"
sign_in @mock_user
#Ability.stub!('can').and_return(true)
end
it "should let me see grids/:g_id/tokens index" do
test_grid = mock_model(Grid)
test_token = mock_model(Token)
Grid.stub!(:find).and_return(test_grid)
Token.stub!(:find).and_return(test_token)
get 'index'
a1 = Ability.new(@mock_user)
a1.can?(:index, Token).should be_true # This line works fine; as it should
puts response.status #This returns 403, which means CanCan::AccessDenied was raised
end
end
Obrigado,
Robbie
Solução
Eu testo o modelo CANCAN separadamente, mas testando o que ele permitirá em quais condições.
Eu acho que se você estiver fazendo coisas como
authorize! :take_over, @the_world
Então eu acho que você deveria estar testando isso no controlador. Não tenho certeza se você precisa testar todas as 6 versões de seus modelos.
Você pode arrancar a habilidade. Pode? Classe e faça com que responda verdadeiro/falso e teste como o controlador lida quando pode (e mais importante) quando não puder continuar.
Outras dicas
Não tenho certeza se isso é tarde demais para você, mas eu apenas encontrei o mesmo problema e resolvi usando o seguinte amostra de código -
before do
@user = Factory.create(:user)
sign_in @user
@abilities = Ability.new(@user)
Ability.stub(:new).and_return(@abilities)
end
end
Eu estalei a habilidade#nova, dando -me uma referência à instância de habilidade que controla o usuário atual. Então, eu posso arrancar habilidades específicas como esta:
@abilities.stub!(:can?).with(:destroy, regatta).and_return(true)
ou dê privilégios de administrador:
@abilities.stub!(:can?).and_return(false)
Semelhante à resposta de Sam, mas na página do Wiki de Cancan nos testes:
Teste do controlador
Se você deseja testar a funcionalidade de autorização no nível um do controlador, é fazer login no usuário que possui as permissões apropriadas.
user = User.create!(:admin => true) # I recommend a factory for this
# log in user however you like, alternatively stub `current_user` method
session[:user_id] = user.id
get :index
assert_template :index # render the template since he should have access
Como alternativa, se você deseja testar o comportamento do controlador independentemente do que está dentro da classe de habilidades, é fácil eliminar a capacidade de qualquer comportamento que você deseja.
def setup
@ability = Object.new
@ability.extend(CanCan::Ability)
@controller.stubs(:current_ability).returns(@ability)
end
test "render index if have read ability on project" do
@ability.can :read, Project
get :index
assert_template :index
end
Se você tiver permissões muito complexas, isso pode levar a muitas possibilidades de ramificação. Se tudo for testado na camada do controlador, poderá levar a testes lentos e inchados. Em vez disso, recomendo manter testes de autorização do controlador e testam a funcionalidade de autorização mais detalhadamente no modelo de habilidade através de testes de unidade, como mostrado na parte superior.
Eu acho que a autorização precisa ser feita principalmente para controladores Para garantir que sua autorização esteja funcionando corretamente com seus controladores. Então, para fazer isso SECO Você pode implementar o seu próprio matcher
para ser usado assim
let!(:user) {create :user}
before { login_user_request user}
it "grants admin access to show action" do
expect{ get :show, {id: user.id} }.to be_authorized
end
it "denies user access to edit action" do
expect{ get :edit, {id: user.id} }.to be_un_authorized
end
e depois implemente esses correspondentes com sua própria maneira de testar como uma solicitação será autorizada ou não
RSpec::Matchers.define :be_authorized do
match do |block|
block.call
expect(response).to be_success
end
def supports_block_expectations?
true
end
end
RSpec::Matchers.define :be_un_authorized do
match do |block|
expect{
block.call
}.to raise_error(Pundit::NotAuthorizedError)
end
def supports_block_expectations?
true
end
end
Por que você não inclui um
can :manage, :all do
user.is_ultrasuper == 1
end
em sua capacidade e depois ter um param IS_ULTRASUPER em um dos seus usuários de acessórios:
one:
id: 1
username: my_username
is_ultrasuper: 1
Em seguida, registre este usuário na configuração de seus testes. Isso nos testes, você poderá fazer qualquer coisa.