Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Abstract Testframework Adapter Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

Minimal setup:

  1. Create a custom adapter class (e.g., 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 { /* ... */ }
    }
    
  2. Register it in infection.json:
    {
        "test_framework": "app\\Adapters\\CustomTestFrameworkAdapter"
    }
    

Debugging tip: Use infection --verbose to inspect adapter execution flow.


Implementation Patterns

Core Workflow

  1. Inheritance: Extend AbstractTestFrameworkAdapter to inherit:

    • CLI argument parsing (ConfigBuilder)
    • Temporary file handling (TemporaryFileHelper)
    • Test result formatting (TestResult objects)
  2. Contract Compliance: Implement:

    • getName()/getVersion() (framework metadata)
    • runTests() (core execution method)
    • hasSyntaxError() (optional, for syntax error detection)
  3. Result Handling: Return an iterable of TestResult objects with:

    • File paths ($file)
    • Test statuses ($status: TestResult::PASSED, TestResult::FAILED, etc.)
    • Optional metadata (e.g., TestResult::withMessage())

Laravel-Specific Tips

  • Service Provider Integration: Bind your adapter to Laravel’s container:
    $this->app->bind(
        Infection\AbstractTestFrameworkAdapter\TestFrameworkAdapterInterface::class,
        app(\App\Adapters\CustomTestFrameworkAdapter::class)
    );
    
  • Configuration: Use Laravel’s config system to override Infection defaults:
    config(['infection.test_framework' => \App\Adapters\CustomTestFrameworkAdapter::class]);
    
  • Artisan Command: Extend Infection’s command for custom logic:
    use Infection\InfectionCommand;
    
    class CustomInfectionCommand extends InfectionCommand
    {
        protected function getAdapter(): TestFrameworkAdapterInterface
        {
            return new CustomTestFrameworkAdapter();
        }
    }
    

Syntax Error Handling (New)

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');
}

Test Parallelization

For Laravel’s parallel testing (e.g., Pest), override runTests() to use parallel():

public function runTests(): iterable
{
    yield from $this->getTestRunner()->parallel();
}

Gotchas and Tips

Pitfalls

  1. Exit Code Mismatches:

    • Frameworks may use non-standard exit codes (e.g., Pest returns 1 for failures, PHPUnit uses 2).
    • Override mapExitCode() to normalize:
      protected function mapExitCode(int $exitCode): int
      {
          return $exitCode === 1 ? TestResult::FAILED : $exitCode;
      }
      
  2. Syntax Error False Positives:

    • The hasSyntaxError() method may flag legitimate test failures as syntax errors.
    • Fix: Combine with test status checks:
      public function hasSyntaxError(string $output): bool
      {
          return str_contains($output, 'ParseError') &&
                 !str_contains($output, 'Test failed');
      }
      
  3. Temporary File Conflicts:

    • Laravel’s cache/storage directories may interfere with Infection’s temp files.
    • Solution: Explicitly set a temp directory:
      $this->setTempDirectory(sys_get_temp_dir() . '/infection');
      
  4. PHPUnit/Pest Version Quirks:

    • PHPUnit 10+ uses attributes (@test), while older versions use annotations.
    • Tip: Use getVersion() to branch logic:
      if (version_compare($this->getVersion(), '9.0', '<')) {
          // Legacy annotation handling
      }
      

Debugging

  • Log Adapter Output: Add debug logs in runTests():
    \Log::debug('Test output:', ['output' => $output]);
    
  • Isolate Tests: Run a single file to test adapter behavior:
    infection --filter=Tests/Feature/ExampleTest.php
    
  • Check Infection’s Cache: Clear cached results if tests appear flaky:
    infection --clear-cache
    

Extension Points

  1. 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;
    }
    
  2. 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;
            }
        }
    }
    
  3. 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');
    }
    

Laravel-Specific Quirks

  • Database Transactions: If your tests use transactions, ensure the adapter respects Laravel’s DatabaseTransactions trait:
    public function runTests(): iterable
    {
        $this->getTestRunner()->withDatabaseTransactions();
        yield from $this->getTestRunner()->run();
    }
    
  • Service Container: Avoid direct Laravel service binding in the adapter—use Infection’s DI container instead:
    $this->container->bind('test.runner', function () {
        return new PestTestRunner();
    });
    

Performance Tips

  • Cache Test Results: Leverage Laravel’s cache to store Infection results:
    $cacheKey = 'infection.results.' . md5($file);
    $results = Cache::get($cacheKey);
    if (!$results) {
        $results = $this->runTestsForFile($file);
        Cache::put($cacheKey, $results, now()->addHours(1));
    }
    
  • Skip Slow Tests: Exclude Laravel’s slow tests (e.g., browser tests) via .infectionignore:
    Tests/Browser/
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata