Great question! When building a Laravel package that leverages Spatie permissions and is tested with Orchestra Testbench, you have to consider that the user model may differ between the application and your tests. This is especially common if your package is agnostic to the user model.
Best-Practice Solution
1. Type-Hint the Contract or the Base Authenticatable
Instead of hard-coding the model in your policy like:
public function viewAny(User $user): bool
You can type-hint the broader contract, but as you noted, the Illuminate\Contracts\Auth\Authenticatable contract doesn't include hasPermissionTo() (which comes from the Spatie trait).
2. Use the Spatie Trait’s Interface (Recommended)
If you want to keep type safety, the best approach is to type-hint the Spatie\Permission\Contracts\Permission or your user class if you know it always implements the trait, but that’s not always practical when writing a package.
3. Rely on Duck Typing (No Type-Hinting)
Given the above, the de facto standard is:
- Don’t type-hint anything on your user.
- Or, at most, hint
Authenticatable if you want minimal interface guarantee.
Your policy method will look like this:
public function viewAny($user): bool
{
return method_exists($user, 'hasPermissionTo')
&& $user->hasPermissionTo(PostPermission::VIEW_ANY->value);
}
Or just, if you're sure only your models use this, and want the test to fail if not:
public function viewAny($user): bool
{
return $user->hasPermissionTo(PostPermission::VIEW_ANY->value);
}
4. How to Get the Model Dynamically?
If you want to dynamically resolve the user model (i.e., from your package config), don’t type-hint at all, and let Laravel inject the authenticated user.
You might use:
$userModel = config('auth.providers.users.model', \App\Models\User::class);
But in policies, you don't manually resolve the model—Laravel will inject the current user instance.
5. Testbench and Spatie Permissions Setup
In your tests/TestCase.php or your setup code, make sure to configure the user model and the Spatie permissions package to use your test model:
protected function getEnvironmentSetUp($app)
{
// Set correct user model
$app['config']->set('auth.providers.users.model', \Workbench\Models\User::class);
// Optionally set spatie permission model configs here as well if customized
}
Summary
- It is perfectly normal not to type-hint the user in your policy when building packages.
- If you want stricter typing, document that your user must implement the Spatie trait/interface.
- Always set the config in your Testbench tests to use your test user model.
- Laravel will inject the correct user model according to your config.
Example Policy
public function viewAny($user): bool
{
return $user->hasPermissionTo(PostPermission::VIEW_ANY->value);
}
This works seamlessly for both your real app and your package tests.
References
Let me know if you have more questions!