簡體   English   中英

在測試Laravel應用程序時,一個加載門如何定義?

[英]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已經啟動,這意味着我無法使用@canGate等驗證用戶權限。

有沒有正確的方法來處理這個問題?

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM