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 for mutation testing, start by installing the package via Composer (though it’s primarily a dependency for adapters):
```bash
composer require infection/abstract-testframework-adapter

First use case: If you’re building a custom Infection adapter for a test framework (e.g., Pest, Laravel’s built-in tests), extend AbstractTestFrameworkAdapter and implement the required methods. Focus on these core steps:

  1. Extend the base class:
    use Infection\AbstractTestFrameworkAdapter\AbstractTestFrameworkAdapter;
    
    class PestAdapter extends AbstractTestFrameworkAdapter
    {
        // Implement required methods
    }
    
  2. Register the adapter in infection.json:
    {
        "test_framework": "PestAdapter",
        "test_framework_adapter": "path/to/PestAdapter"
    }
    
  3. Leverage the new HasSyntaxErrorDetection (if needed) to handle syntax errors in test output:
    use Infection\AbstractTestFrameworkAdapter\HasSyntaxErrorDetection;
    
    class PestAdapter extends AbstractTestFrameworkAdapter implements HasSyntaxErrorDetection
    {
        public function hasSyntaxError(string $output): bool
        {
            return str_contains($output, 'ParseError') || str_contains($output, 'syntax error');
        }
    }
    
  4. Test locally with Infection’s CLI:
    vendor/bin/infection --test-framework=PestAdapter
    

Where to look first:


Implementation Patterns

Core Workflow

  1. Extend AbstractTestFrameworkAdapter: Override methods like runTests(), getName(), and getVersion() to tailor behavior for your framework. Use the base class’s utilities (e.g., createProcess(), mapExitCode()) to avoid reinventing boilerplate.

  2. Handle test execution: Implement runTests() to return an iterable of TestResult objects or a TestResult array. Example for Pest:

    public function runTests(array $files): iterable
    {
        $process = $this->createProcess(['pest', '--testsuite', implode(' ', $files)]);
        $process->run();
        yield from $this->parseOutput($process->getOutput());
    }
    
  3. Parse output: Use TestResult objects to standardize results. For syntax errors, implement hasSyntaxError() to filter malformed test files early:

    public function hasSyntaxError(string $output): bool
    {
        return preg_match('/(ParseError|syntax error)/', $output) === 1;
    }
    
  4. Version compatibility: Use getVersion() to check framework support and branch logic (e.g., PHPUnit 9 vs. 10 output formats). Example:

    public function getVersion(): string
    {
        return '2.0'; // Pest version
    }
    

Laravel-Specific Tips

  • Artisan integration: If using Laravel’s test runner, wrap the adapter in a service provider to inject Laravel-specific paths or configurations:
    public function register()
    {
        $this->app->singleton(PestAdapter::class, function ($app) {
            return new PestAdapter($app['path.tests']);
        });
    }
    
  • Cache test results: Leverage Laravel’s cache to store parsed test results for faster mutation runs:
    use Illuminate\Support\Facades\Cache;
    
    public function runTests(array $files): iterable
    {
        $cacheKey = 'infection:pest:results:' . md5(implode(',', $files));
        if (Cache::has($cacheKey)) {
            yield from Cache::get($cacheKey);
            return;
        }
        // ... execute tests ...
        Cache::put($cacheKey, $results, now()->addHours(1));
        yield from $results;
    }
    

Debugging Patterns

  • Log raw output: Add debug logging for test framework output to diagnose parsing issues:
    use Psr\Log\LoggerInterface;
    
    public function __construct(private LoggerInterface $logger)
    {
    }
    
    protected function parseOutput(string $output): array
    {
        $this->logger->debug('Raw test output:', ['output' => $output]);
        // ... parse logic ...
    }
    
  • Mock processes: Use Laravel’s Process facade or Mockery to test adapter logic without running real tests:
    $mockProcess = Mockery::mock(Process::class);
    $mockProcess->shouldReceive('run')->andReturn(true);
    $mockProcess->shouldReceive('getOutput')->andReturn('Test output');
    $adapter = new PestAdapter($mockProcess);
    

Gotchas and Tips

Pitfalls

  1. Exit code mismatches:

    • Frameworks may use non-standard exit codes (e.g., Pest returns 1 for both syntax errors and test failures). Override mapExitCode() to normalize:
      protected function mapExitCode(int $exitCode): int
      {
          return $exitCode === 1 && $this->hasSyntaxError($this->output)
              ? TestFrameworkAdapterInterface::EXIT_CODE_SYNTAX_ERROR
              : parent::mapExitCode($exitCode);
      }
      
    • Tip: Test with php -r 'exit(1);' to verify your mapping handles edge cases.
  2. Syntax error detection false positives:

    • The hasSyntaxError() method may flag legitimate test failures (e.g., FatalErrorException in Pest). Refine the regex or add framework-specific checks:
      public function hasSyntaxError(string $output): bool
      {
          return str_contains($output, 'ParseError') &&
                 !str_contains($output, 'FatalErrorException');
      }
      
  3. Parallelization conflicts:

    • If your framework (e.g., Pest) doesn’t support parallel execution, the adapter’s default sequential runs may cause timeouts. Add a guard:
      public function runTests(array $files): iterable
      {
          if ($this->isParallelMode()) {
              throw new \RuntimeException('Parallel mode not supported for ' . $this->getName());
          }
          // ... sequential logic ...
      }
      
  4. Version skew:

    • The adapter’s getVersion() may not match the installed framework version. Use composer.json or runtime checks:
      public function getVersion(): string
      {
          return file_exists($this->getComposerPath())
              ? json_decode(file_get_contents($this->getComposerPath()), true)['version']
              : 'unknown';
      }
      
  5. Temporary file cleanup:

    • The base adapter handles temp files, but custom logic (e.g., writing test stubs) may leak files. Extend getTemporaryDirectory() or implement onTestEnd():
      protected function onTestEnd(): void
      {
          foreach (glob($this->getTemporaryDirectory() . '/*') as $file) {
              if (is_file($file)) unlink($file);
          }
      }
      

Debugging Tips

  • Enable verbose output: Set the INFECTION_VERBOSE env var to log adapter interactions:
    INFECTION_VERBOSE=1 vendor/bin/infection
    
  • Inspect TestResult objects: Dump results to debug parsing:
    foreach ($this->runTests([$file]) as $result) {
        dump($result->getStatus(), $result->getOutput());
    }
    
  • Test with minimal inputs: Start with a single test file to isolate adapter issues:
    vendor/bin/infection --test-framework=PestAdapter --only-tests=tests/Feature/ExampleTest.php
    

Extension Points

  1. Custom argument handling: Override getAdditionalArguments() to inject framework-specific flags:

    protected function getAdditionalArguments(array $files): array
    {
        return ['--coverage-clover=' . storage_path('logs/clover.xml')];
    }
    
  2. Process customization: Extend createProcess() to modify the underlying Symfony/Process instance:

    protected function createProcess(array $command): Process
    {
        $process = parent::createProcess($command);
        $process->setTimeout(300); // 5-minute timeout
        return $process;
    }
    
  3. Syntax error granularity: Enhance hasSyntaxError() to return line numbers or file paths:

    public function getSyntaxErrors(string $output): array
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope