dimanche 27 décembre 2015

How to test cancancan abilities in controller test with default testing framework (minitest)

In my rails app I have two user roles: 'student' and 'admin'. They have different access authorization to different pages, e.g., 'admin' can access listing users page (index) but 'student' cannot. This is controlled using cancancan.

Now I am writing tests for controllers and, since I have two different roles, (to my knowledge) I need two separate tests for a single behaviour, for example:

test "should get index for admin" do
    sign_in(users(:admin))
    get "index"
    assert_response :success
end

test "should not get index for student" do
    sign_in(users(:student))
    get "index"
    assert_response :unauthorized
end

where sign_in(*) is the method for handing user login (sessions etc.)

As I am considering adding more roles (e.g. 'manager', 'agent'), I need to add new tests for all the controller methods each time I add a role. This is tedious and not "DRY", so I am trying to find a more elegant way to handle this. Here is my first thought:

In my test_helper.rb, I added:

def assert_admin_only(&block)
    sign_in(users(:admin))
    instance_exec(&block)
    assert_response :success
    sign_out

    sign_in(users(:student))
    instance_exec(&block)
    assert_response :unauthorized
    sign_out

    sign_in(users(:agent))
    instance_exec(&block)
    assert_response :unauthorized
    sign_out
end

Then in my test:

test "should get index for admin only" do
    assert_admin_only do
        get "index"
    end
end

So that each time I added a new role, I only have to add a few lines in the test_helper.rb method in order to test the abilities.

However, it does not work as I thought because "Functional tests allow you to test a single controller action per test method." according to Rails API DOC, while in my code I was firing two actions or even more. For some reason I can't figure out, it seems that the sign_in and sign_out don't actually change the current_user (although they are working perfectly in real requests), which essentially make my attempt fail.

In a word, I want to reuse my tests for different user roles so that I don't have to waste my time copying and pasting existing codes every time I add a new role. I would really appreciate it if you guys could provide some brilliant ideas.

Aucun commentaire:

Enregistrer un commentaire