My app uses the spatie/laravel-permission package to manage roles and permissions, and PestPHP for testing.
It’s normal to set up users, roles, and permissions for each test. I created SetupRoles
trait to accomodate this.
trait SetUpRoles
{
/**
* @var \Spatie\Permission\Models\Role
*/
protected $superAdminRole;
// the rest of code
public function setUpRoles(): void
{
$this->app
->make(\Spatie\Permission\PermissionRegistrar::class)
->forgetCachedPermissions();
$this->superAdminRole = Role::findOrCreate(RoleEnum::SUPERADMIN->value, 'web');
// the rest of code
}
}
Normally, we would do this manually in each test file:
uses(Tests\Feature\Concerns\SetUpRoles::class);
beforeEach(function () {
$this->setupRoles();
});
However, we would prefer to automatically run setUpRoles()
for each test, similar to how the RefreshDatabase
trait works.
To achieve this, we need to extend setUpTraits
in our tests/TestCase
like so:
namespace Tests;
use Tests\Feature\Concerns\SetUpRoles;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Boot the testing helper traits.
*
* @return array
*/
protected function setUpTraits()
{
$uses = parent::setUpTraits();
if (isset($uses[SetUpRoles::class])) {
/** @disregard */
$this->setUpRoles();
}
}
}
Essentially, this scans all used classes from the test file. If SetUpRoles
trait is detected, it calls the setUpRoles
method.
protected function setUpTraits()
{
$uses = array_flip(class_uses_recursive(static::class));
if (isset($uses[RefreshDatabase::class])) {
$this->refreshDatabase();
}
// ... rest of the code
}
Above is how RefreshDatabase
is called automatically from our test.
Finally, we just need to register our trait inside tests/Pest.php
like this:
uses(
Tests\TestCase::class,
Illuminate\Foundation\Testing\RefreshDatabase::class,
Tests\Feature\Concerns\SetUpRoles::class,
)->in('Feature', 'Unit');
Done. Now, you don’t need to reference the traits in your test files anymore.