I have an Laravel app with about 30 permissions. App also uses plenty of endpoints.
Now I want to write feature tests for each endpoint, checking every permission on it.
There is my question: Should I write separate method for each permission like that:
public function test_if_user_can_create_article_having_articles_index_permission()
{
$user = $this->makeUserWithSuchPermisssion();
$this->actingAs($user)->post('/articles')->assertStatus(403)
}
public function test_if_user_can_create_article_having_articles_create_permission()
{
$user = $this->makeUserWithSuchPermisssion();
$this->actingAs($user)->post('/articles')->assertStatus(201)
}
public function test_if_user_can_create_article_having_users_index_permission()
{
$user = $this->makeUserWithSuchPermisssion();
$this->actingAs($user)->post('/articles')->assertStatus(403)
}
and so on
or should I do it by looping through every permission within action test like that
public function test_if_user_can_create_article()
{
foreach($invalid_permissions as $permission)
{
$user = $this->makeUserWithSuchPermisssion($permission);
$this->actingAs($user)->post('/articles')->assertStatus(403)
}
foreach($ok_permissions as $permission)
{
$user = $this->makeUserWithSuchPermisssion($permission);
$this->actingAs($user)->post('/articles')->assertStatus(201)
}
}
or should I do it in another way? If yes, how you are solving that? I dont want to make multiple manual calls to each endpoint with every permission like that because it will become highly unmaintanable.
public function test_if_user_can_create_article()
{
$route = '/articles';
$this->actingAs($this->createUserWithPermission('article.index'))->post($route)->assertStatus(403);
$this->actingAs($this->createUserWithPermission('users.index'))->post($route)->assertStatus(403);
$this->actingAs($this->createUserWithPermission('users.update'))->post($route)->assertStatus(403);
$this->actingAs($this->createUserWithPermission('article.create'))->post($route)->assertStatus(201)->assertJson(['aaa' => 'bbb']);
}
CodePudding user response:
At the end of the day it is up to you! (Not what you were looking for) You should be testing functionality that adds value to your application to have.
I personally would have 2 higher order tests like this to test 1 account works with the permission and 1 doesn't work and then unit test the function that checks the permission of a mocked user in a few obscure tests.
Now I can't answer exactly what you should test since I don't know how your permissions are implemented. But if you do decide you want to test every single permission (seems like a waste of time to check every negative case) I would group together all the failing cases into 1 test and loop through them like you did in your second example, and then another test for the passing test.
Also make sure though to add the failing permission in the error message which is the second parameter in ->assertStatus(201, $permission_xyz_failed_message)
, as that is something that will add value if it starts to fail with a nice reason.
CodePudding user response:
Your tests are not exactly okay:
- The naming convention is to use
should
instead ofif
and usewhen
. For example,test_if_user_can_create_article_having_articles_index_permission
should betest_user_can_create_article_when_having_articles_index_permission
. - Never, ever, do more than a test inside a test.
test_if_user_can_create_article
should not be doing a loop and testing everything (and worst 2 different cases), because if only one fails, the whole test fails, and it makes no sense, as all the others passed, so it should only fail for that specific case/test. So to fix that, make use of@dataProvider
and create 2 different tests, one that asserts it can create and one that can't. - Related to your XXXX tests about permissions, that is totally fine, it is a feature test, so if you later change the implementation of that authorization, this test should still pass, so it is okay to create 1 test for each case. (I would do the same).
- Related to your last question, do not create that sort of tests, test exactly what you want. In that example, if any fails, you will not know exactly which one, and if you do, you will be failing a test for 1 case or more, when other passed, so split each one as a separate test.