KeironLowe92's avatar

Better way to write this test for a Store controller method?

So I've got a system with 4 roles, Super Admin, Admin, Editor, Viewer. I'm trying to write a test for the store method of a controller that ensures that users can't create users with a higher role than their own. So Admins can't create Super Admins, Editors can't create Super Admins or Admins etc etc.

I've got a working test, but I feel like it could be improved a lot but I'm not sure of another way to write it.

Test below, I'm using a data provider which contains each role with its ID, and an array of expected results when trying to create other roles. In my test, I'm signing in as the role for that test, then fetching the data provider again to get a list of roles to send a request for, which I'm then doing and asserting whether it was successful or not.

It just feels a bit clumsy, and I feel there must be a better way of doing it?

/** @test | @dataProvider authorizedRolesToCreateDataProvider */
public function authorizedUsersCanOnlyCreateUsersOfSameOrLowerRole(int $roleId, array $expectedResults): void
{
    // 1. Sign in as a role
    $currentUser = $this->signInAs($roleId);

    // 2. Send create request for each role
    $roles = $this->authorizedRolesToCreateDataProvider();
    foreach ($roles as $role => $roleData) {
        $shouldBeAuthorized = $expectedResults[$role];

        $response = $this->postJson(
            '/api/user/store',
            $this->generateUserCreationData(['role_id' => $roleData['roleId']])
        );

        if ($shouldBeAuthorized) {
            $response->assertStatus(201);
        } else {
            $response->assertStatus(403);
        }
    }
}

public function authorizedRolesToCreateDataProvider(): array
{
    return [
        'Super Admin' => [
            'roleId' => 1,
            'expectedResults' => [
                'Super Admin' => true,
                'Admin'       => true,
                'Editor'      => true,
                'User'        => true
            ]
        ],
        'Admin' => [
            'roleId' => 2,
            'expectedResults' => [
                'Super Admin' => false,
                'Admin'       => true,
                'Editor'      => true,
                'User'        => true
            ]
        ],
        'Editor' => [
            'roleId' => 3,
            'expectedResults' => [
                'Super Admin' => false,
                'Admin'       => false,
                'Editor'      => true,
                'User'        => true
            ]
        ],
        'User' => [
            'roleId' => 4,
            'expectedResults' => [
                'Super Admin' => false,
                'Admin'       => false,
                'Editor'      => false,
                'User'        => false
            ]
        ]
    ];
}
0 likes
1 reply
jamesfairhurst's avatar

Only change I can see if that you don't need to recall the authorizedRolesToCreateDataProvider as the data is already provided in the function, one benefit of using a provider:

public function authorizedUsersCanOnlyCreateUsersOfSameOrLowerRole(int $roleId, array $expectedResults): void
{
    // 1. Sign in as a role
    $currentUser = $this->signInAs($roleId);

    // 2. Send create request for each role
    foreach ($expectedResults as $role => $shouldBeAuthorized) {
        $response = $this->postJson(
            '/api/user/store',
            $this->generateUserCreationData(['role_id' => $roleId])
        );

        if ($shouldBeAuthorized) {
            $response->assertStatus(201);
        } else {
            $response->assertStatus(403);
        }
    }
}

Please or to participate in this conversation.