exSnake's avatar

In Tinker is True in test is false

This is my DB structure:

permissions
permission_role
roles
role_user
users

I have this Test Method:

/** @test */
    public function an_operator_can_view_changes(){
        $user = factory('App\User')->create();
        $role = Role::firstOrCreate(['name' => 'Operator']);
        $permission = Permission::firstOrCreate(['name' => 'view_changes']);
        $role->givePermissionTo($permission);
        $user->assignRole($role);
        $change = factory('App\Change')->create();
        $this->actingAs($user)
            ->get('/changes')->assertSee($change->id); //403 unauthorized
    }

After assigning Role even if in the DB seem to be everything correct if i try to

dd($user->roles[0]->permission[0]) //has permission to "view_changes"
dd($user->isOperator) //return true
dd($user->can("view_changes") //return false

The same command did on php artisan tinker return true http://prntscr.com/q0zngs (here is the screenshot)

dd($user->can("view_changes") //return true on Tinker

What's happening? I'm struggling with this

0 likes
7 replies
Tray2's avatar

These are always tricky but make sure the role you create Operator is the same in that database and in your code. Running things in phpunit makes them case sensitive evven though they aren't normly.

manelgavalda's avatar

Maybe the $user is not picking up the changes, what happens if you use $user->fresh instead?

dd($user->roles[0]->permission[0]) //has permission to "view_changes"
$user = $user->fresh();
dd($user->isOperator) //return true
dd($user->can("view_changes") //return false
exSnake's avatar

It's all correct for what i see, there is something wrong when i check with the can() method because he have the permission to "view_changes" but the can method won't pick it up...

Is it possible that the policies will be loaded after running the test in test but in Tinker they will be loaded first?

Tray2's avatar

Can you show us the User model?

exSnake's avatar

Here it is

class User extends Authenticatable
{
    use Notifiable, HasRoles;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function isAdmin(){
        return $this->hasRole('Admin');
    }

    public function isOperator(){
        return $this->hasRole('Operator');
    }
}

HasRoles Trait

<?php

namespace App;

trait HasRoles
{

    /**
     * A user may have multiple roles.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

    /**
     * Assign the given role to the user. If it's Role class it will be called recursively 
     *
     * @param  string $role
     * @return mixed
     */
    public function assignRole($role)
    {
        if (is_string($role)) {
            return $this->roles()->save(
                Role::whereName($role)->firstOrFail()
            );
        }

        return $this->assignRole($role->name);
    }

    public function removeRole($role){
        if (is_string($role)) {
            return $this->roles()->detach(
                Role::whereName($role)->firstOrFail()
            );
        }

        return $this->removeRole($role->name);
    }

    /**
     * Determine if the user has the given role. If it's Role class it will be called recursively 
     *
     * @param  mixed $role
     * @return boolean
     */
    public function hasRole($role)
    {

        if (is_string($role)) {
            return $this->roles->contains('name', $role);
        }
        if (is_array($role)) {
            foreach ($role as $r) {
                if ($this->hasRole($r)) {
                    return true;
                }
            }
            return false;
        }
        if (isset($role->name)) {
            return $this->hasRole($role->name);
        }


        return !!$role->intersect($this->roles)->count();
    }

    /**
     * Determine if the user may perform the given permission.
     *
     *
     * @param  Permission $permission
     * @return boolean
     */
    public function hasPermission(Permission $permission)
    {
        return $this->hasRole($permission->roles);
    }
}
exSnake's avatar
exSnake
OP
Best Answer
Level 8

After a long while i solved this instantiating user overriding the setUp in the test classes. Didn't know why but now it's fine.

Please or to participate in this conversation.