codenco-dev/eloquent-model-tester
Test Eloquent models in Laravel with codenco-dev/eloquent-model-tester. It verifies database table structures, relationships, fillable and guarded fields, and more using factories and the simple HasModelTester trait, helping you catch model issues early with less boilerplate in your test suite.
Architecture fit. The package is a dev-only, trait-based assertion library that adds a fluent DSL over Laravel's schema builder and Eloquent reflection. It does not touch runtime application code, autoloaded service providers in production, or the request lifecycle — installation is composer require --dev and a use HasModelTester; in a test case. Architecturally this is close to zero-coupling: the only contract is "your models extend Illuminate\Database\Eloquent\Model and your test suite boots the framework." That makes it a safe fit for any standard Laravel monolith or modular app already using PHPUnit/Pest. It is not a fit for projects that abstract Eloquent behind repositories with non-standard persistence, or for packages that must remain framework-agnostic.
Integration feasibility. High. The README demonstrates a working path (RefreshDatabase + modelTestable(Model::class)->assertHasColumns([...])), and the assertion surface maps to things a TPM would actually want asserted as contracts: column presence, fillable/guarded divergence, relation existence, morph relations, scopes, and pivot tables without models. The assertNoGuardedAndFillableFields() and assertCanOnlyFill() helper assertions are the most defensible value — mass-assignment misconfiguration is a real security concern and is otherwise awkward to test idiomatically.
Technical risk. The dominant risk is maintenance, not code. Signals from the repository: 20 stars, zero dependents, a single-maintainer footprint, CI badges still pointing at Travis CI (long deprecated in favour of GitHub Actions), and README links to Laravel 6.x documentation. The README also contains the typo HasModelTestor in most usage examples, which will cause a real "trait not found" error for anyone copy-pasting — a small but telling quality signal. There is no stated Laravel version support matrix, which is the single largest unknown given how much Laravel's test/schema internals have churned between v6 and v12. The "last release 2026-05-08" metadata suggests active-ish publishing, but with zero dependents there is no ecosystem back-pressure to keep it working.
Key questions.
doctrine/dbal-based schema tests or Laravel's Schema::hasColumn?Stack fit. Appropriate for any Laravel 8+ application using PHPUnit or Pest with RefreshDatabase/DatabaseTransactions. It composes cleanly with factories but does not depend on them for most assertions — the relation assertions use reflection on the model class rather than instantiating records, which keeps runtime cost low. Teams using Pest should verify trait usage translates to uses(HasModelTester::class) in the relevant test directory; the README is PHPUnit-only, so Pest support is unverified and should be validated in a spike.
Migration path. This is greenfield addition, not a migration — there is no incumbent to replace. The pragmatic sequencing is: (1) introduce alongside one high-churn model (typically User), (2) run in a single branch to validate framework compatibility, (3) if green, codify a one-test-per-model convention. Do not attempt a bulk retrofit of every model at once; the strict assertions (assertHasOnlyColumns, assertHasOnlyColumnsInFillable) will fail loudly on any model with undocumented columns, and a 200-model rollout will produce an unreviewable red wall.
Compatibility. The two hard dependencies are the Laravel major version and the database driver's introspection capability. Column-type and column-presence assertions run against the live connection, so behaviour differs across SQLite (test default for many teams), MySQL, and PostgreSQL — notably around type normalisation and implicit columns. Any team running the suite against multiple drivers must validate assertions on each, or scope these tests to the CI-only driver. Also verify interaction with parallel test execution (php artisan test --parallel), since schema assertions are database-state reads and are generally safe but unproven here.
Sequencing.
User example verbatim against the pinned Laravel version; confirm trait name resolution (correcting the ModelTestor typo) and at least one relation assertion.Models/*Test.php per model, RefreshDatabase hoisted to the base TestCase, strict assertions reserved for models with stable, intentionally-frozen schemas.tests/Support/ and drop the dependency — this is cheap insurance given the MIT licence and small surface area.Maintenance. Near-zero ongoing cost in the happy path, but the burden is concentrated and deferred: it lands on whoever upgrades Laravel. Because the package introspects framework internals, a Laravel major bump can break assertions without any change on your side. Budget for an explicit "verify model tests" step in every framework upgrade runbook. The real ongoing cost is test-suite ownership — someone must decide, per model, whether $fillable drift should fail CI, and that is a policy commitment, not a tooling one.
Support. Effectively community/self-support. With 20 stars and zero dependents there is no peer community, no Stack Overflow corpus, and no guarantee of issue triage. Plan for internal ownership: a named engineer who can read the assertion source. This is acceptable for a dev-dependency but must be stated explicitly so it isn't discovered during an incident.
Scaling. Test-count scaling is linear and cheap — reflection-based assertions add milliseconds. The scaling concern is cognitive: strict assertions on many models create a large, tightly-coupled contract surface where a single legitimate schema change reddens many tests. Prefer permissive (assertHasColumns) as the default and reserve assertHasOnlyColumns for models where you genuinely want to force a review of every new column. CI parallelisation and multi-driver matrices are the other scaling axes and are unvalidated.
Failure modes.
assertHasOnlyColumns* failing on incidental columns (framework-added, package-added, or migration-ordering artifacts) driving engineers to weaken or skip the tests.HasModelTestor from the README yields an opaque "trait not found" — trivial, but it sets the first-impression tone.Ramp-up. Under an hour for an engineer already comfortable with Eloquent testing — the DSL is fluent and self-documenting, and the README, while typo-riddled and dated, covers the full assertion surface with concrete examples. Budget a half-day for the initial spike including compatibility validation. The bigger ramp-up cost is organisational: agreeing which properties are contractual (columns, fillable, relations) and which are implementation detail, before writing the first test. Recommend a short internal one-pager codifying that convention alongside the adoption PR, so the tests don't become a source of debate in every schema-changing review.
Verdict. Low-cost, low-coupling, genuinely useful for enforcing mass-assignment and schema contracts — worth a scoped pilot on a handful of models. The deciding factor is not the package's capability but its maintenance posture: adopt behind a documented exit plan (vendor the source if it lapses), pin the Laravel version compatibility in CI, and keep strict assertions narrow.
How can I help you explore Laravel packages today?