graham-campbell/testbench
Testing utilities for Laravel packages, built on PHPUnit, Mockery, Orchestral Testbench, and Laravel Testbench Core. Supports Laravel 8–13 and PHP 7.4–8.5, with compatibility for PHPUnit 9–11 to help you run fast, reliable package test suites.
Installation:
composer require --dev graham-campbell/testbench:^6.3
No additional configuration is required.
First Test Case:
Extend GrahamCampbell\TestBench\AbstractTestCase in your test file:
use GrahamCampbell\TestBench\AbstractTestCase;
class ExampleTest extends AbstractTestCase
{
public function testBasicSetup()
{
$this->assertTrue(true);
}
}
Run Tests:
phpunit
For testing a Laravel package, extend GrahamCampbell\TestBench\AbstractPackageTestCase:
use GrahamCampbell\TestBench\AbstractPackageTestCase;
class PackageTest extends AbstractPackageTestCase
{
protected function getPackageProviders($app)
{
return ['YourPackage\\ServiceProvider'];
}
public function testPackageBootstrap()
{
$this->assertTrue(true);
}
}
AbstractAppTestCase for full Laravel app testing.getBasePath() to specify your app’s root directory:
protected static function getBasePath()
{
return __DIR__ . '/../../';
}
AbstractPackageTestCase for isolated package testing.getRequiredServiceProviders() (static method):
protected static function getRequiredServiceProviders()
{
return ['YourPackage\\ServiceProvider'];
}
getPackage() to access your package instance:
$package = $this->getPackage();
$mock = $this->mock('Your\\Contract');
$mock->shouldReceive('method')->once();
assertInJson() for HTTP responses:
$response = $this->call('GET', '/api/endpoint');
$this->assertInJson($response, ['key' => 'value']);
createApplication() with a database configuration:
$app = $this->createApplication();
$app->make('db')->connection()->getSchemaBuilder()->create('table_name', function ($table) {
$table->increments('id');
});
setUp():
$this->app->bind('Your\\Interface', function () {
return $this->mock('Your\\Interface');
});
Use call() for HTTP requests:
$response = $this->call('GET', '/route', ['param' => 'value']);
$this->assertEquals(200, $response->getStatusCode());
Override getMiddleware() in AbstractPackageTestCase:
protected function getMiddleware()
{
return ['YourPackage\\Middleware'];
}
Use expectsEvents() to verify event dispatching:
Event::expectsEvents('Your\\Event');
Mock the queue connection:
$this->app->bind('queue', function () {
return $this->mock('Illuminate\\Contracts\\Queue\\Queue');
});
Use artisan() helper:
$this->artisan('command:name', ['option' => 'value'])
->assertExitCode(0);
Static Methods in V6+:
getBasePath() and getRequiredServiceProviders() are now static in AbstractAppTestCase and AbstractPackageTestCase, respectively. Avoid passing $app as a parameter.protected static function getBasePath() { ... }
protected static function getRequiredServiceProviders() { ... }
PHPUnit Version Conflicts:
phpunit.xml aligns:
<phpunit bootstrap="vendor/autoload.php">
<php>
<ini name="error_reporting" value="-1" />
</php>
</phpunit>
^11.0 or ^9.0 in composer.json.Mockery Assertions:
shouldReceive()) must be called before the method under test. Reorder logic if assertions fail unexpectedly.Database Transactions:
$this->beginDatabaseTransaction();
// Test logic
$this->rollBackDatabaseTransaction();
Service Provider Loading:
getRequiredServiceProviders() is correct.register() method doesn’t assume a loaded app (e.g., no app['config'] calls before boot()).Enable Debug Mode:
Set APP_DEBUG=true in .env.testing or override in setUp():
$this->app->bind('app.debug', function () {
return true;
});
Dump the App: Inspect the Laravel container in tests:
dump($this->app->make('config')->get('app.key'));
Isolate Tests:
Use refreshApplication() to reset the app between tests:
public function tearDown(): void
{
$this->refreshApplication();
parent::tearDown();
}
Check for Conflicts:
Run composer why-not graham-campbell/testbench to detect version conflicts.
Custom Test Cases:
Extend AbstractTestCase to add reusable test logic:
abstract class CustomTestCase extends AbstractTestCase
{
protected function mockHttpClient()
{
return $this->mock('GuzzleHttp\\Client');
}
}
Override Fixtures:
Customize the test environment by overriding createApplication():
protected function createApplication()
{
$app = parent::createApplication();
$app['config']->set('app.timezone', 'UTC');
return $app;
}
Add Assertions:
Use PHPUnit’s @method annotation to add custom assertions to your test case:
/**
* @method void assertInJson(array $response, array $expected)
*/
class CustomAssertionsTest extends AbstractTestCase { ... }
Integration with Pest:
TestBench works with Pest via pest.php:
uses(GrahamCampbell\TestBench\AbstractTestCase::class)->in('Tests');
Environment Files:
TestBench uses .env.testing by default. Copy .env to .env.testing and customize:
APP_ENV=testing
DB_CONNECTION=sqlite_memory
Encryption Key: TestBench sets a dummy key automatically. For encrypted data tests, override:
$this->app['config']->set('app.key', 'base64:your-key-here');
Queue Workers: Disable queues in tests to avoid background processes:
$this->app['queue.worker'] = $this->mock('Illuminate\\Queue\\Worker');
How can I help you explore Laravel packages today?