Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Eloquent Model Tester Laravel Package

codenco-dev/eloquent-model-tester

Laravel dev-only helper to test Eloquent models: verify table structure/columns, fillable vs guarded attributes, and model relationships. Works with PHPUnit and model factories, integrates easily in your model test classes.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require codenco-dev/eloquent-model-tester --dev
    
  2. Generate Test File

    php artisan make:test Models/UserTest
    
  3. Add Trait In your test file, include the HasModelTester trait:

    use CodencoDev\EloquentModelTester\HasModelTester;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    use Tests\TestCase;
    
    class UserTest extends TestCase
    {
        use RefreshDatabase, HasModelTester;
    }
    
  4. First Test Test basic model structure:

    public function test_model_structure()
    {
        $this->modelTestable(User::class)
            ->assertHasColumns(['id', 'name', 'email'])
            ->assertHasTimestampsColumns();
    }
    

Implementation Patterns

Common Workflows

1. Schema Validation

  • Basic Column Check
    $this->modelTestable(User::class)
         ->assertHasColumns(['id', 'name', 'email']);
    
  • Strict Column Check (ensures no extra columns exist)
    $this->modelTestable(User::class)
         ->assertHasOnlyColumns(['id', 'name', 'email', 'created_at', 'updated_at']);
    

2. Fillable/Guarded Validation

  • Check Fillable Fields
    $this->modelTestable(User::class)
         ->assertHasColumnsInFillable(['name', 'email']);
    
  • Strict Fillable Check (ensures only specified fields are fillable)
    $this->modelTestable(User::class)
         ->assertHasOnlyColumnsInFillable(['name', 'email']);
    
  • Check Guarded Fields
    $this->modelTestable(User::class)
         ->assertHasColumnsInGuarded(['password']);
    

3. Relation Testing

  • One-to-One Relations
    $this->modelTestable(User::class)
         ->assertHasHasOneRelation(Phone::class);
    
  • Many-to-One Relations
    $this->modelTestable(Phone::class)
         ->assertHasBelongsToRelation(User::class);
    
  • Many-to-Many Relations
    $this->modelTestable(User::class)
         ->assertHasManyToManyRelation(Role::class);
    
  • Custom Keys for Relations
    $this->modelTestable(Customer::class)
         ->assertHasBelongsToRelation(Category::class, 'category', 'category_id');
    

4. Morph Relations

  • Morph Many
    $this->modelTestable(Post::class)
         ->assertHasHasManyMorphRelation(Comment::class, 'comments');
    
  • Morph One
    $this->modelTestable(User::class)
         ->assertHasMorphOneRelation(Image::class, 'avatar');
    

5. Pivot Tables

  • Test Pivot Table Schema
    $this->tableTestable('role_user')
         ->assertHasColumns(['user_id', 'role_id', 'created_at']);
    

6. Query Scopes

  • Check Scope Existence
    $this->modelTestable(User::class)
         ->assertHasScope('popular');
    

Integration Tips

  1. Group Tests by Model Organize tests in tests/Feature/Models/ with a ModelNameTest file per model.

  2. Leverage TestCase Base Class Extend TestCase with HasModelTester and RefreshDatabase in tests/TestCase.php to avoid repetition.

  3. Combine Assertions Chain assertions for comprehensive validation:

    $this->modelTestable(User::class)
         ->assertHasColumns(['id', 'name', 'email'])
         ->assertHasTimestampsColumns()
         ->assertHasHasOneRelation(Phone::class)
         ->assertHasScope('popular');
    
  4. Use in CI/CD Integrate into your test suite to catch schema/relation changes early.


Gotchas and Tips

Pitfalls

  1. Missing Timestamps in Strict Checks

    • assertHasOnlyColumns() requires explicit created_at/updated_at if they exist in the DB.
    • Fix: Always include timestamps if they’re part of your schema.
  2. Case Sensitivity in Column Names

    • Assertions are case-sensitive. Ensure column names match the DB schema exactly.
  3. Soft Deletes Require Trait

    • assertHasSoftDeleteTimestampColumns() only works if the model uses SoftDeletes.
    • Fix: Add use SoftDeletes; to your model.
  4. Many-to-Many Pivot Assumptions

    • The package assumes standard pivot table naming (model1_model2). Override with custom names if needed:
      $this->modelTestable(User::class)
           ->assertHasManyToManyRelation(Role::class, 'user_roles');
      
  5. Morph Relations Need Exact Keys

    • Custom keys for morph relations must match the model’s morphMap or getMorphClass() logic.

Debugging Tips

  1. Inspect Schema Dynamically Use Laravel’s Schema::getColumnListing() to verify column names:

    dd(\Schema::getColumnListing('users'));
    
  2. Check Relation Definitions For custom relations, verify the model’s belongsTo, hasOne, etc., definitions match the test assertions.

  3. Enable Database Logging Add to config/logging.php to debug schema issues:

    'channels' => [
        'database' => [
            'driver' => 'database',
            'table' => 'log',
            'level' => 'debug',
        ],
    ],
    

Extension Points

  1. Custom Assertions Extend the trait to add model-specific assertions:

    trait CustomModelTester
    {
        public function assertHasCustomField(string $field)
        {
            return $this->assertDatabaseHas('users', [$field => 'expected_value']);
        }
    }
    
  2. Override Default Keys For non-standard foreign keys, pass explicit keys to relation methods:

    $this->modelTestable(Customer::class)
         ->assertHasBelongsToRelation(Category::class, 'category', 'custom_category_id');
    
  3. Combine with Factories Use factories to seed test data for relation tests:

    public function test_has_one_relation()
    {
        $user = User::factory()->create();
        Phone::factory()->create(['user_id' => $user->id]);
    
        $this->modelTestable(User::class)
             ->assertHasHasOneRelation(Phone::class);
    }
    
  4. Skip Tests Conditionally Use Laravel’s skipIf() or skipUnless() to conditionally run tests (e.g., for optional features):

    public function test_optional_feature()
    {
        $this->skipUnless(config('features.enable_optional_feature'));
    
        $this->modelTestable(Model::class)
             ->assertHasScope('optional_scope');
    }
    
  5. Mock Database for Complex Scenarios Use DatabaseMigrations or DatabaseTransactions for tests requiring complex state:

    use Illuminate\Foundation\Testing\DatabaseMigrations;
    
    class ComplexRelationTest extends TestCase
    {
        use DatabaseMigrations, HasModelTester;
    
        public function test_has_many_through()
        {
            // Setup complex relations...
            $this->modelTestable(Customer::class)
                 ->assertHasHasManyThroughRelation(Order::class, Location::class);
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle