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

Testbench Core Laravel Package

orchestra/testbench-core

Testbench Core is a testing framework for Laravel package development. It boots a Laravel application inside your package repository, letting tests run Artisan commands, migrations, factories, and routes, with releases matching Laravel 6 through 13.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you're installing. orchestra/testbench-core is the engine, not the product. It ships the test-case base classes (Orchestra\Testbench\PHPUnit\TestCase), the Concerns\* traits, Attributes\*, the Foundation\* bootstrapper, the testbench console binary and the package:test / serve commands. Most package authors should composer require --dev orchestra/testbench (metapackage: core + a Laravel skeleton). Install testbench-core alone only when you are wiring up your own skeleton or replacing the default one.

Pin the matrix before anything else. The core version is tightly coupled to the framework:

Laravel testbench-core
9.x 7.x
10.x 8.x
11.x 9.x
12.x 10.x
13.x 11.x
composer require --dev orchestra/testbench:^11.0   # Laravel 13
composer require --dev orchestra/testbench:^10.0   # Laravel 12

Where to look first, in this order:

  1. Orchestra\Testbench\PHPUnit\TestCase (and the alias Orchestra\Testbench\TestCase) — the base class everything hangs off.
  2. src/Concerns — WithWorkbench, WithFixtures, InteractsWithMockery, database concerns. These are the real API.
  3. src/Attributes — WithConfig, WithFixtures, UsesVendor, and friends.
  4. testbench.yaml in your package root — the config for the throwaway "host app".
  5. vendor/bin/testbench — an artisan-like binary bound to a booted application that uses your package.

First use case — boot a Laravel app inside your package repo, register your provider, and hit a route:

use Orchestra\Testbench\Attributes\WithConfig;
use Orchestra\Testbench\PHPUnit\TestCase as Testbench;

final class WebhookRouteTest extends Testbench
{
    protected function getPackageProviders($app): array
    {
        return [\Acme\Billing\BillingServiceProvider::class];
    }

    protected function getEnvironmentSetUp($app): void
    {
        $app['config']->set('billing.secret', 'test-secret');
        $app['config']->set('database.default', 'testing');
    }

    #[WithConfig('billing.retries', 3)]
    public function test_webhook_is_accepted(): void
    {
        $this->postJson('/billing/webhook', ['id' => 'evt_1'])
            ->assertOk();
    }
}

If that passes, you have migrations, factories, routing, artisan and config all working — Testbench is doing its job.

Implementation Patterns

Provider registration is the contract. getPackageProviders() is what makes your package "installed" for the duration of a test. Put it in a shared base class (tests/TestCase.php) and never repeat it per test. If a test needs a subset of behaviour, override it locally rather than branching on env vars.

Config: three layers, pick deliberately.

  • getEnvironmentSetUp($app) — global for the whole test class. Best for database.default, app.key, queue.default.
  • #[WithConfig('key', $value)] — per-test, declarative, visible in the method signature. This is the pattern to prefer for one-off toggles.
  • defineEnvironment/inline $this->app['config'] inside the test — acceptable for values computed at runtime.

Migrations and factories. For a package that ships migrations, keep the workbench app responsible for them (testbench.yaml → migrations, seeders) and use RefreshDatabase in tests that need a clean schema. WithLaravelMigrations is available when you need the framework's own tables (jobs, users) alongside yours. Note testbench.yaml supports seeders: true to run the seeder as part of the workbench setup — handy for serve, but do not rely on it in unit tests.

Fixtures over hand-rolled setup. #[WithFixtures] / Orchestra\Testbench\Concerns\WithFixtures loads a fixture file for the test, which keeps large config or DB dumps out of the test body. It is --parallel-aware since 9.20/10.11/11.0.1 — but only on those patch versions or later.

Test the console layer with remote(). When you need a genuinely separate process (config cache, artisan output that mutates globals, exit codes), use Orchestra\Testbench\remote() rather than $this->artisan(). It also handles serializable closures correctly since 10.6.2. For in-process command tests, $this->artisan('acme:sync')->assertExitCode(0) remains the fast path.

The workbench loop is the day-to-day win. vendor/bin/testbench serve boots a real Laravel app pointing at your package so you can click through routes without creating a throwaway project; TESTBENCH_USER_MODEL is injected for you. vendor/bin/testbench package:test runs your suite with the right bootstrapping. vendor/bin/testbench alone drops you into a Tinker-like REPL with your package loaded — the fastest way to answer "is this actually bound?".

Pest: uses(Orchestra\Testbench\PHPUnit\TestCase::class)->in(__DIR__); — the core test case is PHPUnit-based, Pest inherits it fine.

Parallel runs. --parallel works, but anything that writes to a shared path (fixtures, sqlite files, storage disks) needs per-process separation. Since 10.5, Testbench cleans up temporary SQLite files created via journal_mode, but your own temp artifacts are your problem.

Extension points worth knowing: Orchestra\Testbench\Foundation\Application::create() for bootstrapping outside a test case, uses_default_skeleton() to check whether you're on the bundled skeleton, and testbench.yaml for supplying a custom skeleton when your package needs a specific app shape.

Gotchas and Tips

Version skew produces useless errors. A testbench-core one minor off from the framework range typically surfaces as a BindingResolutionException or a missing method deep in the container, never as "version mismatch". Add orchestra/testbench to CI against the lowest and highest Laravel version you claim to support.

#[WithConfig]'s defer semantics have churned. 9.22.0/10.14.0/11.3.0 began loading config after boot to allow service providers to merge config; 9.22.1/10.14.1/11.3.1 reverted the deferred behaviour. Practical rule: if the config value must be visible before providers boot (app.providers, DB connections, cache store), pin the patch version and assert it inside your test rather than trusting the docblock. Use defer: false explicitly if you need the pre-boot path.

v11 removed the annotation API. @define-env, @environment-setup, @define-db, @define-route, and laravel_migration_path() are gone. Migrate to attributes/methods before upgrading. Orchestra\Testbench\Foundation\Env was also removed in 11.0 but restored (with flushState()) in 11.5 — if you subclassed it, re-check on 11.5+.

PHPUnit majors move fast here. 10.13/11.2 added PHPUnit 13 support; 10.4/10.5 tracked 12.2/12.3. If you override setUp()/tearDown() always call parent:: and match the signature, or the suite silently skips your bootstrapping.

exit() in a test kills the whole run. Under pnctl-enabled tests, use Orchestra\Testbench\terminate() or Orchestra\Testbench\bail() instead of exit().

Static state leaks between tests. Testbench flushes Illuminate\Support\Str, JsonResource/JsonApiResource, FormRequest strict mode, and the validator — but only on versions that ship those fixes. If your package sets static flags, flush them yourself in tearDown(); otherwise you get order-dependent failures that vanish on --filter.

#[UsesVendor] before boot is fragile. The 10.10/7.60 fix addressed an unbooted-application BindingResolutionException. Don't combine #[UsesVendor] with custom getEnvironmentSetUp() logic that itself requires a booted app.

package:test + Collision 8.9.4+ lost the --without-cache option; fixed in 9.22/10.14/11.3. If you see an "unknown option" failure in CI, it's a dependency bump, not your code.

Never ship testbench-core as a runtime dependency. It's require-dev. A production autoload that references Orchestra\Testbench\* classes will explode on install for users.

Debugging recipe: vendor/bin/testbench to poke at bindings; $this->app->make(...) inside the test; --filter plus --debug for lifecycle questions; and when a provider "isn't loading", dump $this->app->getLoadedProviders() — usually you forgot getPackageProviders() in the class you're actually extending.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route