contributte/tester
Contributte Tester integrates Nette Tester into your project with a ready-to-use setup and tooling. Install via Composer and follow the included docs to run and manage automated tests on PHP 8.2+ with Nette 3.2+.
Installation:
composer require --dev contributte/tester
Ensure your phpunit.xml includes the package’s bootstrap:
<phpunit>
<autoload>
<classmap suffix=".php"/>
</autoload>
<extensions>
<extension class="Contributte\Tester\Extensions\TesterExtension"/>
</extensions>
</phpunit>
First Test:
Create a test file (e.g., tests/Unit/ExampleTest.php) and use the Tester facade:
use Contributte\Tester\Tester;
use Tester\Assert;
class ExampleTest extends \PHPUnit\Framework\TestCase
{
public function testBasicAssertion()
{
$tester = new Tester();
Assert::true(true, 'This should pass');
}
}
Run Tests:
./vendor/bin/phpunit
Tester Class: Core testing utility with assertions, mocking, and helpers.Tester\Helpers: Laravel-specific assertions (e.g., assertRouteIs(), assertSession()).Tester\Environment: Manage test environments, temp directories, and cleanup.Tester\ContainerBuilder: Build and mock Laravel’s service container dynamically.Leverage Tester\Helpers for Laravel HTTP assertions:
use Contributte\Tester\Tester;
use Tester\Helpers;
class ApiTest extends \PHPUnit\Framework\TestCase
{
public function testGetUser()
{
$tester = new Tester();
$response = $tester->get('/api/user/1');
Helpers::assertRouteIs('users.show', $response);
Helpers::assertJson($response, [
'id' => 1,
'name' => 'John Doe'
]);
}
}
Use ContainerBuilder to mock Laravel services:
use Contributte\Tester\Tester;
use Contributte\Tester\ContainerBuilder;
class UserServiceTest extends \PHPUnit\Framework\TestCase
{
public function testCreateUser()
{
$tester = new Tester();
$container = ContainerBuilder::buildWith([
'services' => [
'App\Services\UserService' => fn() => new class {
public function create(string $name): string {
return "User: $name";
}
}
]
]);
$userService = $container->get('App\Services\UserService');
$result = $userService->create('Alice');
$tester->assertSame('User: Alice', $result);
}
}
Use Tester\Environment to manage test directories and cleanup:
use Contributte\Tester\Tester;
use Contributte\Tester\Environment;
class FileSystemTest extends \PHPUnit\Framework\TestCase
{
protected function setUp(): void
{
Environment::setup([
'testDir' => __DIR__ . '/tmp',
'purge' => true // Auto-cleanup after tests
]);
}
public function testFileCreation()
{
$tester = new Tester();
file_put_contents('test.txt', 'Hello');
$tester->assertFileExists('test.txt');
}
}
Compare outputs against stored snapshots:
use Contributte\Tester\Tester;
use Tester\Assert;
class SnapshotTest extends \PHPUnit\Framework\TestCase
{
public function testUserJson()
{
$tester = new Tester();
$user = ['id' => 1, 'name' => 'Alice'];
$json = json_encode($user, JSON_PRETTY_PRINT);
Assert::snapshot($json, 'users/alice.json');
}
}
Laravel Artisan Testing:
Use Tester\Helpers::runArtisan() to test CLI commands:
Helpers::runArtisan('migrate:fresh')
->assertExitCode(0)
->assertOutputContains('Migrated');
Database Transactions:
Combine with Laravel’s RefreshDatabase trait:
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends \PHPUnit\Framework\TestCase
{
use RefreshDatabase;
public function testUserCreation()
{
$tester = new Tester();
$response = $tester->post('/users', ['name' => 'Bob']);
$tester->assertDatabaseHas('users', ['name' => 'Bob']);
}
}
Custom Assertions:
Extend Tester\Assert for domain-specific checks:
use Tester\Assert;
class CustomAssertions
{
public static function assertValidEmail(string $email)
{
Assert::match('/^[^\s@]+@[^\s@]+\.[^\s@]+$/', $email);
}
}
PHP 8.5+ Deprecations:
Liberator (used for reflection) may fail with setAccessible() calls. Update to the latest version (^0.5) or patch locally:
// Workaround for PHP 8.5+
if (method_exists($reflectionProperty, 'setAccessible')) {
$reflectionProperty->setAccessible(true);
} else {
$reflectionProperty->setAccessible(true); // Fallback
}
Test Isolation:
Environment::purge(true) may not clean up all files (e.g., symlinks). Use Environment::skip() for problematic directories:
Environment::setup(['skip' => ['node_modules', 'storage/logs']]);
Laravel-Specific Gaps:
Tester\Helpers cautiously for these cases.Mocking Limitations:
ContainerBuilder cannot mock final classes/methods by default. Use bypassFinals:
$container = ContainerBuilder::buildWith([
'bypassFinals' => true,
'services' => [...]
]);
Assertion Conflicts:
Tester\Assert with Laravel’s assert() or Pest’s expect(). Stick to one style per test file.Enable Verbose Output:
Set the VERBOSE environment variable to debug test failures:
VERBOSE=1 ./vendor/bin/phpunit
Inspect Test Environment: Dump the test directory structure:
use Contributte\Tester\Environment;
public function testEnvironment()
{
$tester = new Tester();
$tester->dump(Environment::getTestDir());
}
Snapshot Mismatches: Update snapshots manually with:
./vendor/bin/tester snapshots:update
Custom Testers:
Extend Tester for project-specific needs:
class AppTester extends Tester
{
public function assertApiResponse($response, int $expectedStatus)
{
$this->assertSame($expectedStatus, $response->getStatusCode());
$this->assertJson($response);
}
}
Global Helpers:
Register custom helpers in Tester\Helpers:
use Tester\Helpers;
Helpers::add('assertCustomRule', function ($value, $rule) {
// Custom logic
});
CI/CD Integration: Add a custom PHPUnit listener for test reporting:
use PHPUnit\Runner\AfterLastTestHook;
class TesterListener implements AfterLastTestHook
{
public function executeAfterLastTest(): void
{
// Post-test actions (e.g., upload artifacts)
}
}
Register in phpunit.xml:
<listeners>
<listener class="TesterListener"/>
</listeners>
Snapshot Testing: Disable for slow tests by excluding snapshot files:
Assert::snapshot($data, 'file.json', false); // Skip snapshot
Container Mocking:
Cache container builds in setUp() to avoid rebuilding:
private $container;
protected function setUp(): void
{
$this->container = ContainerBuilder::buildWith([...]);
}
Parallel Tests:
Use Environment::setup(['testDir' => sys_get_temp_dir()]) for parallel test runs to avoid conflicts.
How can I help you explore Laravel packages today?