sampoyigi/testbench
Laravel testbench helpers for package development: quickly boot a minimal app, configure service providers, run migrations, and write reliable integration tests. Lightweight scaffolding to speed up local CI-style testing for your Laravel packages.
Installation Require the package via Composer in your Laravel project:
composer require sampoyigi/testbench --dev
Publish the configuration (if applicable) with:
php artisan vendor:publish --provider="Sampoyigi\Testbench\TestbenchServiceProvider"
First Use Case: Basic HTTP Test Write a simple HTTP test using Laravel’s native testing syntax, enhanced by Testbench:
use Sampoyigi\Testbench\Facades\Testbench;
use Tests\TestCase;
class UserLoginTest extends TestCase
{
public function test_user_can_login()
{
Testbench::actingAsUser(); // Custom helper (if provided)
$response = $this->post('/login', [
'email' => 'test@example.com',
'password' => 'password'
]);
$response->assertStatus(200);
}
}
Where to Look First
Sampoyigi\Testbench\Facades\Testbench for utility methods like actingAsUser(), mockService(), or assertDatabaseState().Sampoyigi\Testbench\Traits\* for reusable test behaviors (e.g., RefreshesDatabase, InteractsWithTime).testbench:run), inspect config/testbench.php for configuration options.Use Testbench’s fluent interface to structure tests hierarchically, reducing boilerplate:
Testbench::test('User Profile')
->describe('GET /profile', function () {
$this->get('/profile')
->assertStatus(200)
->assertJsonStructure(['id', 'name', 'email']);
})
->describe('PUT /profile', function () {
$this->put('/profile', ['name' => 'Updated Name'])
->assertStatus(200)
->assertJson(['name' => 'Updated Name']);
});
Leverage Testbench’s database utilities for isolated tests:
Testbench::test('Order Creation')
->withDatabaseTransactions()
->it('creates an order', function () {
$response = $this->post('/orders', ['product_id' => 1]);
$response->assertCreated();
$this->assertDatabaseHas('orders', ['product_id' => 1]);
});
Simulate third-party services (e.g., payment gateways) without real API calls:
Testbench::test('Payment Processing')
->mock('Stripe', function ($mock) {
$mock->shouldReceive('charge')
->once()
->andReturn(['status' => 'succeeded']);
})
->it('processes a payment', function () {
$this->post('/payments', ['amount' => 100])
->assertStatus(200);
});
Test CLI commands with Testbench’s helpers:
Testbench::test('Artisan Commands')
->it('runs a custom command', function () {
$this->artisan('testbench:generate:test', ['name' => 'UserTest'])
->expectsOutput('Test generated successfully.')
->assertExitCode(0);
});
Test event listeners or broadcasts:
Testbench::test('Event Broadcasting')
->listensTo('OrderPlaced')
->it('broadcasts the event', function () {
event(new OrderPlaced());
$this->assertBroadcasting('orders.placed');
});
Facade Method Conflicts
Avoid naming custom test methods test() or describe() to prevent collisions with Testbench’s fluent methods. Use descriptive names like runLoginTest() instead.
Database State Leaks
If using withDatabaseTransactions(), ensure no tests rely on shared state. Reset factories or seeders between tests:
Testbench::test('Database Isolation')
->beforeEach(function () {
User::factory()->create(['name' => 'Test User']);
})
->it('does not leak data', function () {
$this->assertDatabaseCount('users', 1);
});
Mocking Quirks Testbench’s mocking may override Laravel’s native mocking. Prefer explicit mocks:
// Avoid:
$this->mock('Stripe', ...);
// Use instead:
Testbench::mock('Stripe', ...);
Configuration Overrides
If the package publishes config, ensure your .env.testing overrides are applied:
php artisan config:clear
after publishing the config.
Test Output Logging Enable verbose output for failing tests:
phpunit --verbose
or use Testbench’s debug mode:
Testbench::debug(true);
Isolated Test Environments For flaky tests, run tests in parallel with:
phpunit --parallel
and ensure Testbench’s isolation features (e.g., withFreshDatabase()) are used.
Dependency Conflicts
If tests fail due to version mismatches, pin dependencies in composer.json:
"require-dev": {
"sampoyigi/testbench": "1.0.*",
"laravel/testbench": "^10.0"
}
Custom Assertions Extend Testbench’s assertions by creating a trait:
use Sampoyigi\Testbench\Traits\TestbenchAssertions;
trait CustomAssertions {
public function assertResponseHasError($response, $field) {
$response->assertJsonStructure(['errors' => [$field]]);
}
}
class UserTest extends TestCase {
use TestbenchAssertions, CustomAssertions;
}
Test Helpers
Add reusable test helpers to app/Helpers/TestbenchHelper.php:
if (!function_exists('createTestUser')) {
function createTestUser() {
return User::factory()->create(['email' => 'test@example.com']);
}
}
CI/CD Integration Configure GitHub Actions or GitLab CI to run tests with Testbench:
# .github/workflows/tests.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install --dev
- run: php artisan testbench:run
Skip Redundant Tests
Use Testbench’s skipIf() to conditionally run tests:
Testbench::test('Feature X')
->skipIf(app()->environment('production'))
->it('runs only in non-production', function () {
// Test logic
});
Parallel Test Execution Split tests by feature into separate files and run in parallel:
phpunit --group=auth --parallel
Sensitive Data in Tests
Avoid hardcoding secrets. Use .env.testing:
STRIPE_SECRET=test_sk_123
and load it in phpunit.xml:
<env name="APP_ENV" value="testing"/>
Test Data Sanitization Clear sensitive test data after execution:
Testbench::test('Cleanup')
->afterEach(function () {
DB::table('users')->where('email', 'test@example.com')->delete();
});
How can I help you explore Laravel packages today?