orchestra/testbench
Orchestra Testbench is the de-facto Laravel testing helper for package development. It boots a lightweight Laravel app for your package’s tests, making it easy to run PHPUnit/Pest suites with proper service providers, config, and environment setup.
Installation:
composer require --dev orchestra/testbench
Add to composer.json under require-dev:
"orchestra/testbench": "^11.0"
Basic Test Structure:
Create a test class extending Orchestra\Testbench\TestCase (or Orchestra\Testbench\PHPUnit\TestCase for PHPUnit):
use Orchestra\Testbench\TestCase;
class ExampleTest extends TestCase
{
public function test_basic()
{
$this->assertTrue(true);
}
}
First Use Case: Test a package service provider:
public function test_service_provider()
{
$this->withWorkbench(function (Workbench $workbench) {
$workbench->loadPackages([__DIR__.'/../vendor/package-name']);
$this->assertTrue(app()->has('package-service'));
});
}
orchestra/testbench-core for custom test skeletons.testbench.yaml for global test settings (e.g., seeders, database).$this->withWorkbench(function (Workbench $workbench) {
$workbench->loadPackages([__DIR__.'/../vendor/package-name']);
// Test logic here
});
$workbench->disableDefaultServiceProviders();
testbench.yaml:
seeders: true
migrations: true
Or manually:
$this->withWorkbench(function (Workbench $workbench) {
$workbench->loadMigrationsFrom([__DIR__.'/../database/migrations']);
$workbench->runMigrations();
$workbench->runSeeders();
});
WithFixtures trait:
use Orchestra\Testbench\Concerns\WithFixtures;
class FixtureTest extends TestCase
{
use WithFixtures;
protected function getFixturesPath()
{
return __DIR__.'/fixtures';
}
}
$mock = Mockery::mock('alias:YourService');
$mock->shouldReceive('method')->once();
$this->app->instance('YourService', $mock);
$mock = Mockery::mock('partial', 'YourClass');
$mock->shouldReceive('methodToMock')->andReturn('stubbed');
$response = $this->get('/test-route');
$response->assertStatus(200);
$this->post('/submit', ['field' => 'value'])
->assertRedirect('/success');
Orchestra\Testbench\Workbench:
class CustomWorkbench extends Workbench
{
protected function configure()
{
$this->mergeConfigFrom(__DIR__.'/config/custom.php', 'testbench');
}
}
$this->withWorkbench(CustomWorkbench::class, function (CustomWorkbench $workbench) {
// ...
});
Test commands with:
$exitCode = Artisan::call('command:name', ['option' => 'value']);
$this->assertEquals(0, $exitCode);
Publish and listen to events:
Event::fake();
$listener = new YourListener();
Event::assertListeningTo(YourEvent::class, $listener);
Execute remote commands (e.g., queue workers):
$output = Orchestra\Testbench\remote(function () {
return shell_exec('php artisan queue:work --once');
});
Ensure WithFixtures is compatible with --parallel flag in PHPUnit:
# testbench.yaml
parallel: true
flushState() or extend TestCase:
use Orchestra\Testbench\Concerns\FlushesStates;
class MyTest extends TestCase
{
use FlushesStates;
}
$this->flushStates();
$this->withWorkbench(function (Workbench $workbench) {
$workbench->useDatabase('sqlite');
$workbench->setUpDatabase($this);
});
BindingResolutionException if providers bind the same service.#[UsesVendor] attribute or manually resolve:
$this->app->bind('conflicting-service', function () {
return new YourService();
});
$this->withWorkbench(function (Workbench $workbench) {
$workbench->loadMigrationsFrom([...]);
$workbench->runMigrations();
$this->loadFixtures();
});
@define-env, @define-db) no longer work.testbench.yaml or setUp():
protected function setUp(): void
{
parent::setUp();
putenv('DB_CONNECTION=sqlite');
}
phpunit --verbose
Or in phpunit.xml:
<php>
<env name="TESTBENCH_DEBUG" value="1"/>
</php>
$this->withWorkbench(function (Workbench $workbench) {
dump($workbench->getLoadedPackages());
dump($workbench->getConfiguredProviders());
});
Orchestra\Testbench\flushStates();
Extend TestCase:
class CustomAssertionsTest extends TestCase
{
protected function assertPackageVersion($package, $expected)
{
$actual = Orchestra\Testbench\package_version($package);
$this->assertEquals($expected, $actual);
}
}
Use WithFixtures with JSON/YAML fixtures:
# fixtures/users.yml
users:
- id: 1
name: Test User
Configure testbench.yaml:
parallel:
enabled: true
isolation: true
Override core behavior by extending Orchestra\Testbench\Workbench:
class CustomWorkbench extends Workbench
{
public function customMethod()
{
// ...
}
}
testbench.yaml Overridesdatabase section:
database:
connection: sqlite
migrations: database/migrations
seeders: true
providers:
disable: [App\Providers\AppServiceProvider]
.env.testbench for test-specific configs.setUp():
putenv('APP_ENV=testing');
bootstrap.php loads Testbench:
require __DIR__.'/vendor/autoload.php';
$
How can I help you explore Laravel packages today?