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 Laravel Package

orchestra/testbench

Orchestra Testbench is the de-facto helper for writing tests for Laravel packages, providing a lightweight Laravel application skeleton so you can verify your package's service providers, routes, and config.

View on GitHub
Deep Wiki
Context7

Getting Started

Testbench is development infrastructure: it boots a Laravel application inside your package’s test suite so you can exercise providers, routes, commands, migrations, config merging, views, and assets without maintaining a separate Laravel app. Day-to-day leverage is high once the version matrix is aligned; friction is mostly in matching Testbench to Laravel/PHPUnit and understanding that this is not a runtime dependency.

Minimal setup:

  1. Install the Testbench major that matches your Laravel version:
composer require --dev orchestra/testbench:^10.0

Use ^9 for Laravel 11, ^10 for Laravel 12, ^11 for Laravel 13. Check the release notes because Testbench Core and PHPUnit minimums often move faster than the Laravel constraint.

  1. Create a package test case extending Testbench:
<?php

namespace YourPackage\Tests;

use Orchestra\Testbench\TestCase as BaseTestCase;
use YourPackage\YourServiceProvider;

abstract class TestCase extends BaseTestCase
{
    protected function getPackageProviders($app): array
    {
        return [YourServiceProvider::class];
    }

    protected function getEnvironmentSetUp($app): void
    {
        $app['config']->set('database.default', 'testing');
        $app['config']->set('database.connections.testing', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);
    }
}
  1. Write a normal Laravel test and run it through PHPUnit, Pest, or Testbench’s package runner:
vendor/bin/phpunit
vendor/bin/pest
vendor/bin/testbench package:test

Where to look first:

  • tests/TestCase.php: provider registration and environment setup.
  • testbench.yaml: package discovery, providers, migrations, seeders, Workbench config.
  • workbench/: a local Laravel app for manual smoke tests and browser/HTTP checks.
  • The package service provider: if a feature cannot be tested, ask whether the provider is actually loading it in the Testbench app.

First useful use case: register the package provider, set a config value, hit a package route or run a package command, and assert the result. That replaces the old “create a throwaway Laravel app, install the package via path repository, and test manually” loop.

Implementation Patterns

Provider-driven bootstrapping. Almost every package test starts with getPackageProviders(). Keep it explicit rather than relying on package discovery:

protected function getPackageProviders($app): array
{
    return [
        YourPackage\YourServiceProvider::class,
        YourPackage\TestingServiceProvider::class,
    ];
}

Config and environment setup. Use getEnvironmentSetUp() for baseline test config. Use attributes for test-specific overrides:

use Orchestra\Testbench\Attributes\WithConfig;

#[WithConfig('services.example.endpoint', 'https://example.test')]
public function test_it_uses_configured_endpoint(): void
{
    // ...
}

For config timing, remember that newer Testbench versions load #[WithConfig] after the application has booted by default so providers can merge config first. Use defer: false only when the config must exist before provider boot.

Database tests. Use Laravel’s normal testing traits plus Testbench’s migration helpers:

use Illuminate\Foundation\Testing\RefreshDatabase;

protected function defineDatabaseMigrations(): void
{
    $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}

For package migrations, this is usually cleaner than copying migrations into a host app. If your package ships Laravel migrations, loadLaravelMigrations() is available. testbench.yaml can also declare migrations and seeders: true when the skeleton should seed automatically.

Workbench for manual and integration testing. The workbench/ directory is the day-to-day replacement for “install this package in a fresh Laravel app.” Use it for:

  • manual browsing and HTTP smoke tests;
  • checking published assets, views, and routes;
  • running package install commands against a realistic Laravel app;
  • verifying service provider behavior outside PHPUnit.

Typical loop:

vendor/bin/testbench workbench:serve
vendor/bin/testbench package:test
vendor/bin/testbench package:purge-skeleton

Use package:purge-skeleton when the skeleton or vendor symlink gets stale; it is the reset button.

Artisan command tests. Use Laravel’s command assertions rather than shelling out to php artisan:

public function test_install_command(): void
{
    $this->artisan('your-package:install')
        ->expectsOutput('Package installed.')
        ->assertSuccessful();
}

Testbench gives the command a booted Laravel app with your provider registered. If the command depends on vendor package discovery, use #[UsesVendor] on the test class.

Pest integration. The same TestCase works with Pest:

uses(YourPackage\Tests\TestCase::class)->in(__DIR__);

Attributes as workflow shortcuts. Prefer attributes over deprecated PHPUnit annotations:

  • #[WithConfig(...)] for targeted config changes.
  • #[UsesVendor] when the test needs vendor discovery/symlinked vendor behavior.
  • #[WithFixtures] to auto-load a fixture file for the test.
  • Route/middleware/environment attributes where they keep the TestCase from becoming a grab bag.

Isolation helpers. For tests that need process-level behavior, Testbench exposes Orchestra\Testbench\remote() for running isolated commands and terminate()/bail() for pnctl-enabled tests. These are extension points for package installers, long-running commands, and CLI integration tests, not everyday unit tests.

Gotchas and Tips

Version alignment is the first failure mode. Testbench majors track Laravel majors, but the release notes show tighter coupling to Testbench Core, PHPUnit, and minimum Laravel patch versions. A package that passes on Laravel 12.40 may fail on 12.28 because Testbench requires a newer framework. Pin Testbench deliberately and run the matrix in CI; do not let composer update silently choose a Testbench major.

Do not use php artisan inside package tests. There is no host app’s artisan binary in the package context. Use $this->artisan(...), vendor/bin/testbench, or Testbench’s console helpers. Shelling out to an external Laravel app bypasses the booted test application and hides provider/config bugs.

Skeleton and symlink state can go stale. Workbench relies on a generated skeleton and, in some flows, a vendor symlink. On Windows, symlink removal has historically been fragile. If tests behave as if old providers, config, or assets are present, run vendor/bin/testbench package:purge-skeleton and rebuild Workbench before debugging deeper.

Static state leaks between tests. Testbench flushes a lot: Eloquent model states, Str, validator state, JsonResource state, and more in recent releases. Your package can still leak its own static caches, singleton overrides, or config mutations. Add explicit teardown for package-level static state, especially when using --parallel.

#[WithConfig] timing changed. In newer versions it is applied after application boot by default to allow providers to merge configuration first. If you are testing a provider that reads config during register(), you may need defer: false; if you are testing normal config merging, leave the default.

#[UsesVendor] is for vendor-discovery tests. It is not a general “make it work” attribute. It exists because some package behavior depends on the app being booted with vendor discovery/symlinked vendor context. If a test fails with a BindingResolutionException around UsesVendor, check the Testbench/Core patch level — this has been fixed in several releases.

Parallel testing needs clean fixtures. WithFixtures and --parallel compatibility has been patched over time. If fixtures or SQLite journal files collide, check the Testbench patch version and avoid sharing mutable fixture files across parallel workers.

testbench.yaml is not a substitute for explicit tests. It configures the skeleton and package runner. It does not assert your provider registered a binding or your command output is correct. Use it to reduce boilerplate; keep behavioral assertions in PHPUnit/Pest.

Debugging checklist. When a package test fails, inspect in this order: Testbench/Laravel version compatibility, getPackageProviders() contents, getEnvironmentSetUp() config, testbench.yaml providers/migrations/seeders, stale Workbench skeleton, then static state leakage. Use --filter on package:test and a minimal TestCase before adding traits or attributes.

Extension points worth knowing. Custom skeletons, Orchestra\Testbench\Foundation\Env::flushState(), package_version_compare(), php_version_compare(), and remote() closures are useful for packages that test installers, version-dependent behavior, or process isolation. They are power tools; most packages should stay with a simple TestCase, provider registration, and RefreshDatabase.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo