Как обрабатывать тестирование вызовов конечных точек с различными разрешениями

#php #laravel #testing #permissions #phpunit

Вопрос:

У меня есть приложение Laravel с примерно 30 разрешениями. Приложение также использует множество конечных точек.

Теперь я хочу написать тесты функций для каждой конечной точки, проверяя все разрешения на нее.

Вот мой вопрос: должен ли я писать отдельный метод для каждого такого разрешения:

 
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)
}

 

и так далее

или я должен сделать это, зациклившись на каждом разрешении в рамках такого теста действий

 
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)
    }
    
}

 

или мне следует сделать это по-другому? Если да, то как вы решаете эту проблему?
Я не хочу совершать несколько ручных вызовов к каждой конечной точке с каждым подобным разрешением, потому что это станет очень неудобным.

 
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']);
    
}

 

Ответ №1:

В конце концов, это зависит от вас! (Не то, что вы искали) Вы должны тестировать функциональность, которая повышает ценность вашего приложения.

Я лично провел бы 2 таких теста более высокого порядка, чтобы проверить, работает ли 1 учетная запись с разрешением, а 1 не работает, а затем модульно протестировать функцию, которая проверяет разрешение издевающегося пользователя в нескольких неясных тестах.

Теперь я не могу точно ответить, что вам следует протестировать, так как я не знаю, как реализованы ваши разрешения. Но если вы решите, что хотите проверить каждое отдельное разрешение (кажется пустой тратой времени проверять каждый отрицательный случай), Я бы сгруппировал все неудачные случаи в 1 тест и прошелся по ним, как вы сделали во втором примере, а затем провел еще один тест для прохождения теста.

Также убедитесь , что вы добавили разрешение на сбой в сообщение об ошибке, которое является вторым параметром ->assertStatus(201, $permission_xyz_failed_message) , так как это добавит ценность, если оно начнет отказывать по уважительной причине.

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

1. спасибо за ваш ответ. Приложение, которое у меня есть, было разработано ранее очень неопытным разработчиком. Приложение содержит много беспорядка, в том числе странную, легко нарушаемую проверку разрешений. Вот почему я думаю, что было бы хорошо вызывать каждую конечную точку с разными разрешениями, поскольку can политики, используемые в этих конечных точках, очень легко нарушить. И потому, что приложение горит в течение нескольких недель… вы знаете 😉

Ответ №2:

Ваши тесты не совсем в порядке:

  • Соглашение об именовании заключается в использовании should вместо if и использовании when . Например, test_if_user_can_create_article_having_articles_index_permission должно быть test_user_can_create_article_when_having_articles_index_permission .
  • Никогда, никогда не делайте больше, чем тест внутри теста. test_if_user_can_create_article не следует делать цикл и тестировать все (и худшие 2 разных случая), потому что, если только один из них завершится неудачей, весь тест завершится неудачей, и это не имеет смысла, так как все остальные прошли, поэтому он должен завершиться неудачей только для этого конкретного случая/теста. Поэтому, чтобы исправить это, используйте @dataProvider и создайте 2 разных теста, один из которых утверждает, что он может создавать, а другой-нет.
  • Что касается ваших тестов XXXX о разрешениях, это совершенно нормально, это тест функций, поэтому, если вы позже измените реализацию этой авторизации, этот тест все равно должен пройти, поэтому можно создать 1 тест для каждого случая. (Я бы сделал то же самое).
  • В связи с вашим последним вопросом, не создавайте такого рода тесты, проверяйте именно то, что вы хотите. В этом примере, если какой-либо из них завершится неудачно, вы не будете точно знать, какой именно, и если вы это сделаете, вы провалите тест для 1 случая или более, когда другие пройдут, поэтому разделите каждый из них как отдельный тест.

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

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

2. Да, это будет функциональный тест для каждого случая. Если ваш тип разрешений просто a string , вы можете отправить эту строку методу, который создаст пользователя, а затем протестировать любой URL-адрес (также отправленный), который вам нужен. Вот как вы можете использовать @dataProvider s поверх упомянутого.