orchestra/testbench-core
Orchestra Testbench Core is the foundation for testing Laravel packages. It boots a lightweight Laravel app inside your package so you can run artisan commands, migrations, routing, and more, with compatibility across Laravel 6–12.
Installation:
composer require orchestra/testbench-core --dev
Ensure version compatibility with your Laravel version (e.g., orchestra/testbench-core:^11.0 for Laravel 13).
Basic Test Structure:
Create a test class in tests/ (e.g., Feature/ExampleTest.php) and extend Orchestra\Testbench\TestCase:
use Orchestra\Testbench\TestCase;
class ExampleTest extends TestCase
{
public function test_basic()
{
$this->assertTrue(true);
}
}
First Use Case: Load a Laravel application and test a package:
use Orchestra\Testbench\TestCase;
class PackageTest extends TestCase
{
protected function getPackageProviders($app)
{
return ['YourPackage\\ServiceProvider'];
}
public function test_package_functionality()
{
$this->assertTrue(true); // Replace with actual assertions
}
}
getPackageProviders(): Register package service providers.getEnvironmentSetUp(): Configure environment (e.g., database, cache).getApplication(): Customize the Laravel application instance.getPackageProviders() to load your package’s providers:
protected function getPackageProviders($app)
{
return [
'YourPackage\\Providers\\AuthServiceProvider',
'YourPackage\\Providers\\RouteServiceProvider',
];
}
#[WithConfig] attribute or getEnvironmentSetUp() to merge configs:
use Orchestra\Testbench\Attributes\WithConfig;
#[WithConfig('your-package')]
class ConfigTest extends TestCase { ... }
Or manually:
protected function getEnvironmentSetUp($app)
{
$app['config']->set('your-package.key', 'value');
}
Artisan::call() to run migrations:
public function setUp(): void
{
parent::setUp();
Artisan::call('migrate', ['--path' => 'tests/migrations']);
}
WithFixtures trait or seeders in getEnvironmentSetUp():
use Orchestra\Testbench\Concerns\WithFixtures;
class FixtureTest extends TestCase
{
use WithFixtures;
protected $fixtures = 'tests/fixtures/users_table.php';
}
Execute commands directly:
$this->artisan('your:command', [
'--option' => 'value',
])
->assertExitCode(0);
get(), post(), etc., helpers:
$response = $this->get('/api/endpoint');
$response->assertStatus(200);
orchestra/testbench-browser-kit or orchestra/testbench-dusk for advanced interactions:
$this->browser()->visit('/login')->type('email', 'test@example.com')->press('Login');
Enable parallel tests in phpunit.xml:
<phpunit ...>
<extensions>
<extension class="Orchestra\Testbench\Parallel\ParallelExtension" />
</extensions>
</phpunit>
Ensure WithFixtures is compatible (use flushState() if needed).
getPackageAliases() to override Laravel’s default providers:
protected function getPackageAliases($app)
{
return [
'mail' => 'YourPackage\\MailManager',
];
}
.env via getEnvironmentSetUp():
protected function getEnvironmentSetUp($app)
{
$app['config']->set('database.default', 'sqlite_memory');
putenv('DB_DATABASE=testbench');
}
public function tearDown(): void
{
parent::tearDown();
\Illuminate\Support\Facades\Str::flushStates();
\Illuminate\Validation\Validator::flushStates();
}
orchestra/workbench to preview your package locally:
composer require orchestra/workbench --dev
./vendor/bin/workbench serve
Configuration Loading Timing:
#[WithConfig] now loads after the application boots (default defer: true). Use defer: false if you need early config:
#[WithConfig('your-package', defer: false)]
Parallel Testing Quirks:
WithFixtures may fail in parallel mode. Use flushState() or disable parallelism for fixture-heavy tests.Service Provider Binding Conflicts:
getPackageAliases().Database Transactions:
protected function getEnvironmentSetUp($app)
{
$app['db']->setDefaultConnection('sqlite_memory');
$app['db']->connection()->disableSharedSchemaBuild();
}
Deprecated Annotations:
@define-env, @environment-setup, etc. Use getEnvironmentSetUp() instead.Artisan Command Failures:
$this->artisan('migrate')
->expectsQuestion('confirm', 'yes')
->assertExitCode(0);
Configuration Overrides:
$this->assertEquals('expected', config('your-package.key'));
Service Provider Loading:
$this->assertArrayHasKey('YourPackage\\ServiceProvider', $app->getLoadedProviders());
State Leaks:
\Illuminate\Support\Facades\Str::flushStates();
\Illuminate\Validation\Validator::flushStates();
Custom TestCase Traits:
Extend Orchestra\Testbench\TestCase to add reusable logic:
trait WithCustomAssertions
{
protected function assertPackageConfig($key, $expected)
{
$this->assertEquals($expected, config("your-package.$key"));
}
}
Dynamic Configuration:
Use testbench.yaml to centralize test configurations:
seeders: true
providers:
- YourPackage\\ServiceProvider
Mocking Facades:
Override facades in getEnvironmentSetUp():
$app->instance(\Illuminate\Contracts\Auth\Factory::class, MockAuthFactory::class);
Custom Artisan Commands:
Extend Orchestra\Testbench\Foundation\Console\TerminatingConsole for custom command handling.
Skip Migrations in CI:
Use phpunit.xml to exclude migration-heavy tests:
<phpunit ...>
<filter>!MigrationsTest</filter>
</phpunit>
Cache Configuration: Disable caching for faster iterations:
$this->artisan('config:clear');
Parallel Test Splitting: Group tests by concern to balance parallel workloads:
#[Group('auth')]
class AuthTest extends TestCase { ... }
How can I help you explore Laravel packages today?