Weave 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

Unit Test Case Laravel Package

fintem/unit-test-case

A lightweight base test case for Laravel and PHP packages that simplifies unit testing setup. Extend it to get ready-made helpers, cleaner assertions, and consistent bootstrapping, so you can focus on writing meaningful tests instead of repeating boilerplate configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require --dev fintem/unit-test-case:^2.2

Before writing a single test, do three things:

  1. Read composer.json in the package — and check its release history. It's a one-class package, so the constraints tell you everything: which PHP versions, which phpunit/phpunit range, and whether the class lives under src/ with PSR-4 autoloading. The 2.2 release means the package is not a single 2018 dump — verify what you're actually getting before assuming the worst, but equally don't assume active maintenance:
composer show -a fintem/unit-test-case   # released versions + dates
composer why-not fintem/unit-test-case   # what would have to be downgraded

If 2.2 is recent but 2.0/2.1 are old, you're on a revived-but-single-maintainer package: pin exactly ("2.2.0", not ^2.2) until you've seen a second release land on a predictable cadence.

  1. Open the class itself (vendor/fintem/unit-test-case/src/...). There is still no documentation worth trusting; the source is short enough to read in a minute. Note exactly which PHPUnit TestCase it extends and which protected helpers/methods it adds — those helpers are the entire value proposition. Pay particular attention to any mock-building helper, because 2.2 changed its failure mode (see Gotchas).

  2. Write one throwaway test that extends it and run it:

vendor/bin/phpunit --filter SmokeTest --debug

That confirms autoloading, PHPUnit compatibility, and whether the base class expects a Laravel application instance (which it most likely does not, given the "Unit" naming).

Your first real use case is a pure unit test — a value object, a formatter, a small service with constructor-injected collaborators — i.e. exactly the tests you'd otherwise write against PHPUnit\Framework\TestCase but where you want the package's shared helpers (mock container, assertion shortcuts, fixture setup) without booting Laravel.

Implementation Patterns

Two base classes, deliberately separated. The most important integration pattern is to keep this package's test case for framework-free tests and Laravel's own Illuminate\Foundation\Testing\TestCase for anything touching the container, DB, HTTP, or queues. In tests/:

// tests/Unit/TestCase.php
namespace Tests\Unit;

use Fintem\UnitTestCase\...; // whatever the base is called

abstract class TestCase extends \Fintem\UnitTestCase\BaseTestCase
{
    // local shared helpers only
}
// tests/Feature/TestCase.php
namespace Tests\Feature;

abstract class TestCase extends \Illuminate\Foundation\Testing\TestCase
{
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase;
}

Then wire the suites in phpunit.xml:

<testsuites>
    <testsuite name="Unit">
        <directory>tests/Unit</directory>
    </testsuite>
    <testsuite name="Feature">
        <directory>tests/Feature</directory>
    </testsuite>
</testsuites>

This keeps vendor/bin/phpunit --testsuite Unit fast (no app boot) and prevents the common failure mode where someone extends the unit base class and then wonders why $this->get('/') is undefined.

Constructor-argument mocks are now a contract. Since 2.2, the mock helper validates that the constructor arguments you pass actually correspond to the target class's constructor. That turns the helper into a useful refactor detector — but it also means you should pass arguments deliberately rather than as a throwaway:

$mailer = $this->mock(Collaborator::class, [
    $this->mock(Transport::class),   // arg 0 — must exist in the ctor signature
    'prefix',                        // arg 1
]);

Treat any exception here as a genuine message: either your test is constructing the collaborator with a stale shape, or the production constructor changed and the test was silently passing a mock that ignored it.

Inherit, don't decorate. If the package adds helper assertions or mock-building methods, use them through inheritance. Wrapping them in a trait of your own is tempting but you'll fight setUp ordering; the package's parent::setUp() call is part of its contract.

Mockery integration. For collaborator-heavy unit tests, pair the base class with mockery/mockery and reset in tearDown():

protected function tearDown(): void
{
    if ($container = \Mockery::getContainer()) {
        $this->addToAssertionCount($container->mockery_getExpectationCount());
    }
    \Mockery::close();
    parent::tearDown();
}

Data providers for table-driven cases. Works unchanged since it's plain PHPUnit underneath — the package adds nothing that interferes with @dataProvider. If you're migrating constructor-shaped mocks, a provider over [class, args] pairs is the fastest way to find every broken call site in one run.

CI pinning. Whatever the release history says, pin it exactly ("fintem/unit-test-case": "2.2.0") and run your CI matrix against a fixed PHP/PHPUnit pair. A revived package with a behavior-changing minor (2.1 → 2.2 changing error semantics is a good example) is precisely the kind you don't want floating.

Gotchas and Tips

  • 2.2 changes a silent failure into a loud one. Previously, passing a constructor argument the target class didn't declare would typically be ignored — the mock was built and your test passed against a shape that no longer matched production. Now it throws. That's a good change, but it is an upgrade hazard: grep your suite for every mock-helper call that supplies a constructor array before bumping from 2.0/2.1, and expect failures in tests that were quietly wrong. Budget the upgrade as "fix N tests", not "bump the lockfile".

  • The exception surfaces at mock-creation time, which is often inside setUp(). The stack trace will point at the vendor file, not your test, and PHPUnit reports it against whatever test happened to run first. Use vendor/bin/phpunit --debug --filter YourTest::yourMethod so you can see which test's setup triggered it, then read the vendor helper to confirm which positional argument it rejected.

  • Re-verify the abandonment calculus rather than inheriting it. The 2.2 tag contradicts "single 2018 release", so the earlier "copy 30–80 lines and drop the dependency" advice is no longer automatic. Check dates and commit/issue activity: if 2.2 is the only recent tag on an otherwise dormant repo, still fork into your monorepo (or a path repository) — that remains the only sustainable extension point for a package without a maintainer roster. If releases are now regular, keep the dependency and just pin.

  • PHPUnit major-version signature break. PHPUnit 8 introduced void return types on setUp/tearDown; PHPUnit 10 removed TestCase::setUpBeforeClass-era APIs and renamed several methods. A base class that originated pre-2020 may still declare protected function setUp() without : void, producing fatal errors on modern PHPUnit. Confirm the composer constraint matches your PHPUnit, and if you must keep it on an older PHPUnit, add return types in a fork.

  • No service provider, no auto-discovery. Laravel's package discovery does nothing here — there's no config to publish and no facade. Don't waste time looking for config/unit-test-case.php; if it exists, it's likely unused.

  • Autoload-dev confusion. If the class is registered under autoload-dev in the package, it may not be loadable in all contexts (e.g. when your own package is installed as a dependency by another project). Check composer.json and, if needed, add the namespace to your root autoload-dev.

  • Don't extend it for feature tests. If you find yourself adding CreatesApplication or RefreshDatabase to a subclass of this base class, stop — you're building a second Laravel TestCase with extra steps. Use the framework's base class instead.

  • Extension points are just OOP. Override setUp()/tearDown() and always call parent:: first/last respectively; add helper methods as protected on your project-level Tests\Unit\TestCase rather than editing vendor code. If you need to change the new constructor validation (e.g. you depend on the old lenient behaviour), that's a fork, not a subclass.

  • Sanity check after upgrading: run composer outdated and composer why-not fintem/unit-test-case. If Composer had to downgrade PHPUnit or PHP to satisfy it, the cost still exceeds the benefit of a handful of helper methods — the 2.2 fix doesn't change that arithmetic.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource