How to test global scopes in Laravel?

Janez Cergolj • September 28, 2020

Today I'll share some hopefully useful ideas for testing global scope classes in Laravel borrowing code from a real-life example from the open-source project of mine called laravellte.

I use an outside-in TDD approach. First, we'll make sure that visibleTo global scope is applied to the User model. Below is one option how to test that with the help of getGlobalScopes() method.

// tests/Unit/UserModelTest.php

/** @test */
 public function visible_to_global_scope_is_applied()
 {
     $user = UserFactory::new()->create();

     $this->assertInstanceOf(
         VisibleToScope::class,
         $user->getGlobalScopes()['App\Scopes\VisibleToScope']
     );
 }

This test will fail in the first run. Now we have to create a unit test for VisibleToScope, where we test the actual logic of the scope.

// tests/Unit/VisibleToScopeTest.php

/** @see \App\VisibleToScope */
class VisibleToScopeTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function filter_visible_to_owner_restricted_is_true()
    {
         Schema::create('teams', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('owner_id');
        });

      // assertion
    }
}

class Team extends Model
{
    protected $guarded = [];

    protected $casts = [
        'id' => 'integer',
        'owner_id' => 'integer',
    ];

    public $timestamps = false;

    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new VisibleToScope());
    }
}

I omitted most of the code form the test class. It is not vital to know the ins and outs of it. However, there something that needs to be highlighted. The Team class at the bottom. I don't want to use any existing model with applied visibleTo global scope to unit test. Dedicated Team model at the bottom better shows the intent, and the tests are less fragile. I think visibleTo test shouldn't relay on any existing model. As well, I don't want to create a migration for the Team model. I decided to use Schema builder directly inside the test's setUp method.

As you can see testing global scopes in Laravel isn't hard. Arguably using dedicated model isn't the most agreeable solution, but it does the trick. What are your thoughts? Tweet me @jcergolj.

Here is the full visibleTo global scope unit test.
Here is the unit test for the model.