Eloquent scope assertion for testing Laravel applications.

Overview

Eloquent Scope Assertion

Latest Version on Packagist Total Downloads

This package allows you to assert that the scope of a model is called in your tests.

Installation

You can install the package via composer:

composer require tkaratug/eloquent-scope-assertion

Use Case

Let's say you have a complicated conditional query for Orders that is imported to be tested.

  • Get the orders that have not been paid.
  • Sorted by creation date descending.
  • The list could go on.

The above query constraints should be tested at the feature level, so that you have tests like so;

  • user_can_get_orders
  • user_can_get_only_unpaid_orders
  • user_can_get_orders_by_created_at_desc
  • The list could go on.

Since the query is happening in the model scope it would be nice to test the query in the model's unit test and therefore only write the test coverage once.

However, in your feature tests, it's hard to be sure that the model scope with test coverage is actually used in your controller, so you'll likely duplicate that test coverage in your Feature tests in some way. Thus, you make the unit test you write quite meaningless.

As a solution of that, this package triggers an event that contains the name of the scope and model when a model scope is called. In this way, it is quite easy to assert that the event is dispatched with the correct scope and model names in feature tests.

Usage

Add the HasScopeAssertion trait to the TestCase class in order to be able to call assertScopeCalled() method in your feature tests.

Since the ModelScopeCalled event is triggered when a named scope is called, you should fake it in setUp() method.

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Tkaratug\EloquentScopeAssertion\Traits\HasScopeAssertion;
use Illuminate\Support\Facades\Event;
use App\Events\ModelScopeCalled;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;
    use HasScopeAssertion;

    public function setUp(): void
    {
        parent::setUp();

        Event::fake([ModelScopeCalled::class]);
    }
}

Then add the HasScopeWatcher trait in your models to be able to assert its scopes.

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Tkaratug\EloquentScopeAssertion\Traits\HasScopeWatcher;

class Order extends Model
{
    use HasFactory;
    use HasScopeWatcher;

    public function scopeUnpaid(Builder $query): Builder
    {
        return $query->where('is_paid', false);
    }

    public function scopeCreatedAtDesc(Builder $query): Builder
    {
        return $query->latest('created_at');
    }
}

Let's say you want to see only unpaid orders sorted by creation date descending.

The OrderController should be like this;

use App\Models\Order;
use App\Http\Resources\OrderResource;

public OrderController extends Controller
{
    public function index(): void
    {
        $orders = Order::query()
                       ->unpaid()
                       ->createdAtDesc()
                       ->get();

        return OrderResource::collection($orders);
    }
}

Now you can simplify your test coverage in OrderControllerTest like so;

use App\Models\Order;
use Illuminate\Database\Eloquent\Factories\Sequence;

class OrderControllerTest extends TestCase
{
    public function user_can_get_unpaid_orders_sorted_by_creation_date_descenting(): void
    {
        Order::factory()
             ->unpaid()
             ->createMany([
                 ['created_at' => Carbon::parse('7 days ago')],
                 ['created_at' => Carbon::parse('14 days ago')],
                 ['created_at' => Carbon::parse('21 days ago')],
             ]);

        $response = $this->get(route('orders.index'));

        $response->assertOk();

        $this->assertScopeCalled('unpaid', Order::class);
        $this->assertScopeCalled('createdAtDesc', Order::class);
    }
}

If you want to assert how many times a query scope is called, just add a number as third parameter to assertScopeCalled() method.

// The `unpaid` scope must have been called 2 times in tested code.
$this->assertScopeCalled('unpaid', Order::class, 2);

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

You might also like...
Add Social Reactions to Laravel Eloquent Models. It lets people express how they feel about the content. Fully customizable Weighted Reaction System & Reaction Type System with Like, Dislike and any other custom emotion types. Do you react? Orbit is a flat-file driver for Laravel Eloquent
Orbit is a flat-file driver for Laravel Eloquent

Orbit is a flat-file driver for Laravel Eloquent. It allows you to replace your generic database with real files that you can manipulate using the methods you're familiar with.

Collection of the Laravel/Eloquent Model classes that allows you to get data directly from a Magento 2 database.

Laragento LAravel MAgento Micro services Magento 2 has legacy code based on abandoned Zend Framework 1 with really ugly ORM on top of outdated Zend_DB

The missing laravel helper that allows you to inspect your eloquent queries with it's bind parameters

Laravel Query Inspector The missing laravel helper that allows you to ispect your eloquent queries with it's bind parameters Motivations Let's say you

Laravel Quran is static Eloquent model for Quran.

Laravel Quran بِسْمِ ٱللّٰهِ الرَّحْمٰنِ الرَّحِيْمِ Laravel Quran is static Eloquent model for Quran. The Quran has never changed and never will, bec

A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache.

Laravel OTP Introduction A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache. The ca

 Provides a Eloquent query builder for Laravel or Lumen
Provides a Eloquent query builder for Laravel or Lumen

This package provides an advanced filter for Laravel or Lumen model based on incoming requets.

Curso Laravel Eloquent ORM

Curso Laravel Eloquent ORM

Use eloquent joins in Laravel way, with composite key support.

Eloquent Power Joins with Compoships Support This package is an Eloquent Power Joins extension to support Compoships. You can now use joins in Laravel

Releases(1.1.0)
Owner
Turan Karatuğ
Turan Karatuğ
Laravel Dusk provides simple end-to-end testing and browser automation.

Introduction Laravel Dusk provides an expressive, easy-to-use browser automation and testing API. By default, Dusk does not require you to install JDK

The Laravel Framework 1.7k Jan 5, 2023
Github repository dedicated for my YT tutorial which shows how to use testing in Laravel

Testing in Laravel The following documentation is based on my Laravel Testing for Beginners tutorial we’re going to cover the basics of unit tests, fe

Code With Dary 17 Oct 24, 2022
Trait for Laravel testing to count/assert about database queries

counts_database_queries Trait for Laravel testing to count/assert about database queries Installing composer require ohffs/counts-database-queries-tra

null 1 Feb 23, 2022
Providing some testing functionality for Laravel

Laravel TestBench Laravel TestBench was created by, and is maintained by Graham Campbell, and provides some testing functionality for Laravel. It util

Graham Campbell 50 Dec 20, 2022
Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Rubik 4 May 12, 2022
This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

Jonas Staudenmeir 5 Jan 6, 2023
An Eloquent Way To Filter Laravel Models And Their Relationships

Eloquent Filter An Eloquent way to filter Eloquent Models and their relationships Introduction Lets say we want to return a list of users filtered by

Eric Tucker 1.5k Jan 7, 2023
Easy creation of slugs for your Eloquent models in Laravel

Eloquent-Sluggable Easy creation of slugs for your Eloquent models in Laravel. NOTE: These instructions are for the latest version of Laravel. If you

Colin Viebrock 3.6k Dec 30, 2022
Automatically validating Eloquent models for Laravel

Validating, a validation trait for Laravel Validating is a trait for Laravel Eloquent models which ensures that models meet their validation criteria

Dwight Watson 955 Dec 25, 2022
Laravel Ban simplify blocking and banning Eloquent models.

Laravel Ban Introduction Laravel Ban simplify management of Eloquent model's ban. Make any model bannable in a minutes! Use case is not limited to Use

cybercog 879 Dec 30, 2022