[英]How does one load gate defines when testing a Laravel application?
我正在为Laravel应用程序编写测试。 在我的AuthServiceProvider->boot()
,我根据数据库中的权限表使用$gate->define()
定义了许多用户能力。
基本上这个:
foreach ($this->getPermissions() as $permission) {
$gate->define($permission->name, function ($user) use ($permission) {
return $user->hasPermission($permission->name);
});
}
在我的测试中,我正在动态创建权限,但AuthServiceProvider
已经启动,这意味着我无法使用@can
, Gate
等验证用户权限。
有没有正确的方法来处理这个问题?
public function boot(GateContract $gate)
{
parent::registerPolicies($gate);
$gate->before(function($user, $ability) use ($gate){
return $user->hasPermission($ability);
});
}
我没有对此进行过广泛的测试,但它似乎可以通过我的快速测试来实现。
我知道我对这个派对来说有点迟了,但是 - 我自己也遇到了同样的问题因此这个问题没有全面的答案,这是我对同一问题的解决方案(在Laravel 5.3中) :
我在我的app\\Providers\\AuthServiceProvider
得到了这个:
/**
* Register any authentication / authorization services.
*
* @param Gate $gate
*/
public function boot(Gate $gate)
{
$this->registerPolicies();
if (!app()->runningInConsole()) {
$this->definePermissions($gate);
}
}
/**
* @param Gate $gate
*/
private function definePermissions(Gate $gate)
{
$permissions = Permission::with('roles')->get();
foreach($permissions as $permission) {
$gate->define($permission->key, function($user) use ($permission) {
return $user->hasRole($permission->roles);
});
}
}
这在不测试时负责正常的应用程序流程,并在测试时禁用过早的策略注册。
在我的tests/TestCase.php
文件中,我定义了以下方法(注意Gate
指向Illuminate\\Contracts\\Auth\\Access\\Gate
):
/**
* Logs a user in with specified permission(s).
*
* @param $permissions
* @return mixed|null
*/
public function loginWithPermission($permissions)
{
$user = $this->userWithPermissions($permissions);
$this->definePermissions();
$this->actingAs($user);
return $user;
}
/**
* Create user with permissions.
*
* @param $permissions
* @param null $user
* @return mixed|null
*/
private function userWithPermissions($permissions, $user = null)
{
if(is_string($permissions)) {
$permission = factory(Permission::class)->create(['key'=>$permissions, 'label'=>ucwords(str_replace('_', ' ', $permissions))]);
if (!$user) {
$role = factory(Role::class)->create(['key'=>'role', 'label'=>'Site Role']);
$user = factory(User::class)->create();
$user->assignRole($role);
} else {
$role = $user->roles->first();
}
$role->givePermissionTo($permission);
} else {
foreach($permissions as $permission) {
$user = $this->userWithPermissions($permission, $user);
}
}
return $user;
}
/**
* Registers defined permissions.
*/
private function definePermissions()
{
$gate = $this->app->make(Gate::class);
$permissions = Permission::with('roles')->get();
foreach($permissions as $permission) {
$gate->define($permission->key, function($user) use ($permission) {
return $user->hasRole($permission->roles);
});
}
}
这使我能够以多种方式在测试中使用它。 考虑我的tests/integration/PermissionsTest.php
文件中的用例:
/** @test */
public function resource_is_only_visible_for_those_with_view_permission()
{
$this->loginWithPermission('view_users');
$this->visit(route('dashboard'))->seeLink('Users', route('users.index'));
$this->visit(route('users.index'))->assertResponseOk();
$this->actingAs(factory(User::class)->create());
$this->visit(route('dashboard'))->dontSeeLink('Users', route('users.index'));
$this->get(route('users.index'))->assertResponseStatus(403);
}
/** @test */
public function resource_action_is_only_visible_for_those_with_relevant_permissions()
{
$this->loginWithPermission(['view_users', 'edit_users']);
$this->visit(route('users.index'))->seeLink('Edit', route('users.edit', User::first()->id));
$this->loginWithPermission('view_users');
$this->visit(route('users.index'))->dontSeeLink('Edit', route('users.edit', User::first()->id));
}
这在我的所有测试中都可以正常工作。 我希望它有所帮助。
您可以在AuthServiceProvider
执行类似的AuthServiceProvider
首先导入必要的包
use Illuminate\Auth\Access\Gate;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
然后添加此boot()
方法
public function boot(GateContract $gate)
{
parent::registerPolicies($gate);
$gate->define('update-post', function ($user, $post, $isModerator) {
// check if user id equals post user id or whatever
if ($user->id === $post->user->id) {
return true;
}
// you can define multiple ifs
if ($user->id === $category->user_id) {
return true;
}
if ($isModerator) {
return true;
}
return false;
});
// you can also define multiple gates
$gate->define('update-sub', function($user, $subreddit) {
if($user->id === $subreddit->user->id) {
return true;
}
return false;
});
然后在你的控制器中你可以做这样的事情
if (Gate::denies('update-post', [$post, $isModerator])) {
// do something
}
我不确定用什么“正确”方式(如果有的话)来定义测试门。 在查看文档和搜索之后,我找不到相应的答案,但这似乎在Laravel 5.7中有所作为:
在模型工厂状态中定义门:
$factory->state(App\User::class, 'employee', function () {
Gate::define('employee', function ($user) {
return true;
});
return [];
});
由于我们在创建用户时使用'employee'状态,因此此测试功能将同时应用'employee'和'admin'门:
/** @test */
public function an_admin_user_can_view_the_admin_page()
{
$user = factory('App\User')->state('employee')->make();
$this->actingAs($user);
Gate::define('admin', function ($user) {
return true;
});
$this->get('/admin')
->assertOk();
}
我知道这是一个非常古老的问题,但它是搜索的最佳结果,希望可以帮助某人。
别忘了使用门立面:
use Illuminate\Support\Facades\Gate;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.