infection/abstract-testframework-adapter
Interfaces and base classes for building Infection test framework adapters. Provides a common abstraction layer to integrate different PHP test runners with Infection’s mutation testing, making adapters consistent, reusable, and easier to implement.
For Laravel developers integrating Infection mutation testing, start by installing the package in your project’s test environment:
composer require --dev infection/abstract-testframework-adapter
First use case: Extend the base adapter for your test framework (e.g., PHPUnit or Pest). Focus on these files:
AbstractTestFrameworkAdapter (base class)TestFrameworkAdapterInterface (contract)HasSyntaxErrorDetection (new syntax error handling)Minimal setup:
app/Adapters/CustomTestFrameworkAdapter.php):
use Infection\AbstractTestFrameworkAdapter\AbstractTestFrameworkAdapter;
use Infection\AbstractTestFrameworkAdapter\HasSyntaxErrorDetection;
class CustomTestFrameworkAdapter extends AbstractTestFrameworkAdapter implements HasSyntaxErrorDetection
{
public function getName(): string { return 'custom-framework'; }
public function getVersion(): string { return '1.0'; }
public function runTests(): iterable { /* ... */ }
public function hasSyntaxError(string $output): bool { /* ... */ }
}
infection.json:
{
"test_framework": "app\\Adapters\\CustomTestFrameworkAdapter"
}
Debugging tip: Use infection --verbose to inspect adapter execution flow.
Inheritance: Extend AbstractTestFrameworkAdapter to inherit:
ConfigBuilder)TemporaryFileHelper)TestResult objects)Contract Compliance: Implement:
getName()/getVersion() (framework metadata)runTests() (core execution method)hasSyntaxError() (optional, for syntax error detection)Result Handling: Return an iterable of TestResult objects with:
$file)$status: TestResult::PASSED, TestResult::FAILED, etc.)TestResult::withMessage())$this->app->bind(
Infection\AbstractTestFrameworkAdapter\TestFrameworkAdapterInterface::class,
app(\App\Adapters\CustomTestFrameworkAdapter::class)
);
config(['infection.test_framework' => \App\Adapters\CustomTestFrameworkAdapter::class]);
use Infection\InfectionCommand;
class CustomInfectionCommand extends InfectionCommand
{
protected function getAdapter(): TestFrameworkAdapterInterface
{
return new CustomTestFrameworkAdapter();
}
}
Implement HasSyntaxErrorDetection to parse framework-specific syntax errors:
public function hasSyntaxError(string $output): bool
{
return str_contains($output, 'ParseError') ||
str_contains($output, 'syntax error, unexpected');
}
For Laravel’s parallel testing (e.g., Pest), override runTests() to use parallel():
public function runTests(): iterable
{
yield from $this->getTestRunner()->parallel();
}
Exit Code Mismatches:
1 for failures, PHPUnit uses 2).mapExitCode() to normalize:
protected function mapExitCode(int $exitCode): int
{
return $exitCode === 1 ? TestResult::FAILED : $exitCode;
}
Syntax Error False Positives:
hasSyntaxError() method may flag legitimate test failures as syntax errors.public function hasSyntaxError(string $output): bool
{
return str_contains($output, 'ParseError') &&
!str_contains($output, 'Test failed');
}
Temporary File Conflicts:
$this->setTempDirectory(sys_get_temp_dir() . '/infection');
PHPUnit/Pest Version Quirks:
@test), while older versions use annotations.getVersion() to branch logic:
if (version_compare($this->getVersion(), '9.0', '<')) {
// Legacy annotation handling
}
runTests():
\Log::debug('Test output:', ['output' => $output]);
infection --filter=Tests/Feature/ExampleTest.php
infection --clear-cache
Custom Process Creation:
Override createProcess() to inject Laravel-specific env vars:
protected function createProcess(array $command): Process
{
$process = new Process($command);
$process->setEnv([
'APP_ENV' => 'testing',
'DB_CONNECTION' => 'sqlite',
]);
return $process;
}
Result Filtering: Filter results before returning (e.g., exclude Laravel’s internal tests):
public function runTests(): iterable
{
foreach ($this->getTestRunner()->run() as $result) {
if (!str_contains($result->getFile(), 'vendor')) {
yield $result;
}
}
}
Syntax Error Customization:
Extend hasSyntaxError() for Laravel-specific cases (e.g., Blade syntax):
public function hasSyntaxError(string $output): bool
{
return parent::hasSyntaxError($output) ||
str_contains($output, 'Blade syntax error');
}
DatabaseTransactions trait:
public function runTests(): iterable
{
$this->getTestRunner()->withDatabaseTransactions();
yield from $this->getTestRunner()->run();
}
$this->container->bind('test.runner', function () {
return new PestTestRunner();
});
$cacheKey = 'infection.results.' . md5($file);
$results = Cache::get($cacheKey);
if (!$results) {
$results = $this->runTestsForFile($file);
Cache::put($cacheKey, $results, now()->addHours(1));
}
.infectionignore:
Tests/Browser/
How can I help you explore Laravel packages today?