Why PHPStan dectects these errors?

1.6k Views Asked by At

I use LARAVEL 9 and PHPSTAN

I have this simple test method :

public function createAndAuthenticatedAnUser(string $status = 'VALIDATED', bool $isAdmin = false): User
{
    $user = User::factory()->create([
        'status' => $status,
        'is_admin' => $isAdmin
    ])->first();
    $this->actingAs($user);
    return $user;
}

When I run PHPSTAN, I have these errors :

     40     Parameter #1 $user of method Illuminate\Foundation\Testing\TestCase::actingAs() expects
         Illuminate\Contracts\Auth\Authenticatable, Illuminate\Database\Eloquent\Model|null given.
  41     Method Tests\Feature\ValidateRegistrationTest::createAndAuthenticatedAnUser() should return App\Models\User
         but returns Illuminate\Database\Eloquent\Model|null.

For the 1st error : The actingAs expects an User, it is the case.

For the 2nd error : The function returns an User (which extends Authenticatable)

What can be wrong in this code for PHPSTAN ?

1

There are 1 best solutions below

1
On

Firstly don't run PHPStan for unit tests, PHPStan is to check type integrity and other static analytics it can provide. They don't make sense in a unit test.

Secondly your factory calls are wrong. If no count is provided, create() only creates a single model returns it like so. If count is higher than 1, it will return a collection.

User::factory()->create([
        'status' => $status,
        'is_admin' => $isAdmin
    ])

When you then call first(), it calls it on a single users query builder. That returns Model or null.

This problem is related to typehinting and is what PHPStan checks for, actingAs wants an Authenticatable which is your User class. The only thing PHPStan knows, is your first call returns a model or null. This is not type strict, therefor it complains. As Matias pointed out, everything is solved with forcing the typehint, which are needed in many of the more generic functions of Laravel.

/** @var User $user */
$user = User::factory()->create([
    'status' => $status,
    'is_admin' => $isAdmin
]);