graham-campbell/testbench-core
Core testing utilities for Laravel packages, maintained by Graham Campbell. Provides lightweight TestBench components compatible with Laravel 8–13, PHP 7.4–8.5, and PHPUnit 9–12 to simplify package test setup and integration.
Installation:
composer require --dev graham-campbell/testbench-core:^4.3
First Use Case:
Extend a test class with the provided traits (e.g., MockeryTrait, ServiceProviderTrait) to leverage Laravel-specific testing utilities.
use GrahamCampbell\TestbenchCore\Traits\MockeryTrait;
class ExampleTest extends TestCase
{
use MockeryTrait;
public function testMockingExample()
{
$mock = $this->mock('App\Services\ExampleService');
$mock->shouldReceive('process')->once()->andReturn('mocked');
$result = app('App\Services\ExampleService')->process();
$this->assertEquals('mocked', $result);
}
}
Key Entry Points:
MockeryTrait, ServiceProviderTrait, FacadeTrait, DatabaseTrait (for database testing).assertArraySubset or Laravel-specific helpers.Mocking Laravel Components:
MockeryTrait to mock services, repositories, or facades without manual setup.
$this->mock('App\Contracts\PaymentGateway')
->shouldReceive('charge')
->with(100)
->andThrow(new \Exception('Test failure'));
Service Provider Testing:
ServiceProviderTrait to test bindings, macros, or service provider logic.
use GrahamCampbell\TestbenchCore\Traits\ServiceProviderTrait;
class PaymentServiceProviderTest extends TestCase
{
use ServiceProviderTrait;
public function testBindings()
{
$this->assertServiceProviderClass('App\Providers\PaymentServiceProvider');
$this->assertBound('payment.gateway');
}
}
assertServiceProviderClass() to verify the correct provider is registered.Facade Testing:
FacadeTrait to test facades by mocking their underlying classes.
use GrahamCampbell\TestbenchCore\Traits\FacadeTrait;
class NotificationFacadeTest extends TestCase
{
use FacadeTrait;
public function testFacadeMocking()
{
$this->mockFacade('Notification', 'App\Services\NotificationService');
$this->assertEquals('mocked', Notification::send());
}
}
MockeryTrait for granular control over facade methods.Database Testing:
DatabaseTrait (if available in future versions) or manually set up migrations/seeds in tests.
$this->artisan('migrate:fresh');
$this->artisan('db:seed', ['--class' => 'UserSeeder']);
Combine with Laravel TestCase:
Always extend Laravel’s TestCase (or RefreshDatabase/CreatesApplication) alongside TestBench traits.
use Illuminate\Foundation\Testing\TestCase as LaravelTestCase;
class UserTest extends LaravelTestCase
{
use MockeryTrait;
// ...
}
Custom Assertions: Extend the package’s assertions in your test classes:
use GrahamCampbell\TestbenchCore\Traits\AssertionsTrait;
class CustomAssertionsTest extends TestCase
{
use AssertionsTrait;
public function testArraySubset()
{
$this->assertArraySubset(['key' => 'value'], ['key' => 'value', 'extra' => 'data']);
}
}
Test Doubles: Prefer partial mocks for facades/services to avoid over-mocking:
$mock = $this->partialMock('App\Facades\Logger', ['log']);
$mock->shouldReceive('log')->with('error')->once();
Performance: Reuse mocks across test methods where possible to reduce setup overhead:
protected function setUp(): void
{
$this->mock = $this->mock('App\Services\CacheService');
parent::setUp();
}
Mockery Deprecation:
MockeryTrait may trigger deprecation warnings in PHPUnit 12+ (fixed in v4.2.1+).$this->mock('Class')->shouldReceive('method')->andReturn('value');
// Use `->ignoreDeprecations()` if needed (Mockery 1.4+).
Static Method Changes (v4.0+):
getFacadeAccessor() became static in v4.0, breaking older code.// Before v4.0:
$this->getFacadeAccessor();
// After v4.0:
FacadeTrait::getFacadeAccessor();
PHPUnit Version Mismatch:
composer.json:
"require-dev": {
"phpunit/phpunit": "^12.0"
}
Service Provider Traits:
getServiceProviderClass() no longer accepts the app parameter (v4.0+).$providerClass = ServiceProviderTrait::getServiceProviderClass('App\Providers\ExampleProvider');
Database State:
RefreshDatabase trait or manually reset:
public function tearDown(): void
{
Artisan::call('migrate:rollback');
parent::tearDown();
}
Mock Verification:
Use Mockery’s shouldHaveReceived() to debug unexpected calls:
$this->mock->shouldHaveReceived('method')->once();
Facade Root Inspection: Debug facade resolution with:
dd(FacadeTrait::getFacadeRoot('Notification'));
Service Provider Binding: Check bindings with:
$this->assertBound('contract.name');
$this->assertInstanceOf('App\Services\Example', app('contract.name'));
Custom Traits: Extend existing traits to add domain-specific testing logic:
trait CustomTestTrait
{
protected function mockPaymentGateway()
{
return $this->mock('App\Contracts\PaymentGateway')
->shouldReceive('charge')
->andReturn(true);
}
}
Assertion Helpers:
Add custom assertions to the AssertionsTrait:
use PHPUnit\Framework\Assert;
trait CustomAssertions
{
protected function assertResponseHasStatus($expected, $response)
{
Assert::assertEquals($expected, $response->getStatusCode());
}
}
Test Data Factories: Combine with Laravel’s factories for realistic test data:
public function testUserCreation()
{
$user = User::factory()->create();
$this->assertDatabaseHas('users', ['email' => $user->email]);
}
Parallel Testing:
Use PHPUnit’s --parallel flag with TestBench for faster suites:
phpunit --parallel
Test Isolation:
Use beforeApplicationDestroyed() to clean up mocks:
protected function beforeApplicationDestroyed()
{
$this->mockery->close();
}
Legacy Code: For Laravel <8, use v3.4 of TestBench Core for compatibility:
composer require graham-campbell/testbench-core:^3.4 --dev
CI Optimization:
Cache Composer dependencies and use --testdox-html for readable reports:
composer install --prefer-dist --no-interaction
phpunit --testdox-html report.html
How can I help you explore Laravel packages today?