Weave 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

Shared abstractions for Infection test framework adapters. Provides the TestFrameworkAdapter and TestFrameworkAdapterFactory interfaces so custom adapters can translate Infection's test execution requests into target framework commands and return collected test locations.

View on GitHub
Deep Wiki
Context7

Getting Started

What this package actually is: infection/abstract-testframework-adapter is a low-level contract library, not something you "use" directly in an app. In a Laravel codebase, you'll almost never composer require this yourself — it arrives transitively as a dependency of infection/infection and the concrete adapters such as infection/phpunit-adapter or infection/pest-adapter. Your day-to-day interaction is indirect: it defines how Infection talks to Pest/PHPUnit under the hood, and it's the extension point if you ever need to teach Infection about a custom test runner.

Minimal setup: Add Infection to your Laravel project with the adapters you already test with:

composer require --dev infection/infection infection/phpunit-adapter
# or, if your suite runs on Pest:
composer require --dev infection/infection infection/pest-adapter

Then scaffold the config:

vendor/bin/infection --init

This writes infection.json5. Your first real use case is running a mutation test against a single class in a typical Laravel app/ namespace:

vendor/bin/infection \
  --only-covered \
  --filter=app/Services/InvoiceCalculator.php \
  --threads=max \
  --min-msi=60

Where to look first when something goes wrong: The adapter contract is small — TestFrameworkAdapterInterface and AbstractTestFrameworkAdapter in src/, plus the HasSyntaxErrorDetection interface added in 0.5.0. Read those two classes before you debug anything at the Infection level; they tell you exactly what Infection expects from your runner (arg building, exit code mapping, output parsing, and now syntax-error detection).

First meaningful use case in Laravel: Wrap a service or domain class that has solid Pest/PHPUnit coverage — not a controller (too much framework overhead) and not a model (too much DB coupling). A pure calculation or transformation service gives you the fastest, most reliable mutation feedback loop while you validate that your adapter/runner wiring is sound.

Implementation Patterns

You write an adapter only when you own a runner Infection doesn't support. In Laravel-land this is rare but real: e.g., a custom test harness for a legacy module, a wrapper around Pest with a non-standard bootstrap, or a bespoke runner for a domain package. When that happens, extend AbstractTestFrameworkAdapter rather than implementing the interface from scratch:

final class CustomRunnerAdapter extends AbstractTestFrameworkAdapter
{
    public function getName(): string
    {
        return 'CustomRunner';
    }

    public function getVersion(): string
    {
        // Parse from a lockfile or CLI --version output
    }

    public function checkVersion(): void
    {
        // 0.4.0+ requires this; throw on unsupported versions
    }

    protected function getAdditionalArguments(Configuration $config): array
    {
        return ['--coverage-json=' . $config->getCoveragePath()];
    }

    protected function mapExitCode(int $code): TestResult|ExitCode
    {
        // Normalize custom exit codes into Infection's expected enum/result
    }
}

Integrate through infection.json5, not through code. Register the adapter via the testFramework key so Infection's DI container wires it:

{
  "testFramework": "CustomRunner",
  "testFrameworkOptions": "--testsuite=Unit",
  "source": { "directories": ["app"] },
  "mutators": { "@default": true }
}

Use the shared utilities instead of hand-rolling. ConfigBuilder handles CLI argument assembly, TemporaryFileHelper manages temp dirs, and the Filter helpers process source/test file lists. Every hour you spend reimplementing these is an hour you'll spend re-debugging path handling on Windows or in CI.

Adopt HasSyntaxErrorDetection (0.5.0) deliberately. When your runner emits ParseError or equivalent syntax failures, implement hasSyntaxError() to return true so Infection distinguishes a broken source file from a killed mutant. In Laravel projects this matters most when you're mutating files that also carry test-adjacent helpers — a malformed mutation can otherwise masquerade as a "test failure" and quietly skew your MSI:

public function hasSyntaxError(string $output): bool
{
    return str_contains($output, 'ParseError')
        || str_contains($output, 'Syntax error');
}

Treat the adapter as a one-way dependency. Keep framework-specific parsing (XML config, Pest inline syntax, custom JSON reporters) inside the adapter class. Nothing in your app code, your Pest config, or your CI scripts should ever reference infection/abstract-testframework-adapter classes directly. That isolation is what makes it cheap to swap adapters or drop them entirely.

Version sniff and branch parsing. getVersion() isn't decoration — it's your only defense against PHPUnit/Pest output format churn. Parse differently for PHPUnit 9.x vs 10+ vs Pest 2+ because the JUnit/JSON shapes genuinely differ. Keep those branches close to the parser, not scattered across the adapter.

Gotchas and Tips

  • This package is not your API — the adapter is. Direct use statements against its classes in application code are a smell. If you find yourself importing TestLocation or AbstractTestFrameworkAdapter from app/, step back; you're probably building the adapter in the wrong layer.
  • checkVersion() is required since 0.4.0. If you inherited an adapter written against 0.3.x, it will fatal with a missing-method error. Add the method even if it just validates the minimum supported framework version.
  • TestLocation is the replacement for CoverageLineData (0.3.0 BC break). Old tutorials and blog posts still reference CoverageLineData::withMethod(). The modern shape is TestLocation::forMethod() plus public accessors — if you're porting a community adapter, this is the first thing to fix.
  • Exit code mapping is where bugs hide. Override mapExitCode() and be explicit about every code your framework can emit. Laravel test runners sometimes return 0 on partial failures or wrap errors in 1 when a distinct code would be clearer. Map it correctly or mutation results will lie to you.
  • Don't parallelize inside the adapter. Infection already manages --threads. Adding your own concurrency on top produces flaky, hard-to-reproduce failures. Let the framework and Pest's own --parallel handle it — or disable parallelism entirely if you're unsure.
  • Syntax-error detection is opt-in and easy to get wrong. hasSyntaxError() should look at runner output, not source. Overly broad string matching ('error') creates false positives and makes Infection report "syntax error" for ordinary assertion failures. Match framework-specific markers (ParseError, Fatal error: Uncaught) precisely.
  • The package has essentially no test suite of its own. Stars are low and coverage is thin — validate any adapter you write against a real Laravel test suite in a separate scratch project before trusting it. AbstractTestFrameworkAdapter::getTests() is the canonical reference for expected output shape; treat it as your spec.
  • Watch the Laravel-specific integration surface: parallel test databases, transactions rolled back per-test, and Pest's uses(RefreshDatabase::class) can make mutation runs slow even when they're correct. Scope mutation to pure-unit service classes to keep runtimes sane; don't point Infection at feature tests that boot the framework.
  • getAdditionalArguments() is your extension point for CI flags. Inject --coverage-json, --configuration, or --testsuite here rather than in infection.json5 options, so each adapter stays self-documenting.
  • createProcess() is where you set environment and timeouts. If your CI needs APP_ENV=testing, DB_CONNECTION=sqlite, or a hard timeout, do it here — not in .env files that Infection's subprocesses might not load.
  • Maintenance risk is real. Last substantive features landed around 2021–2024, and there's no sign of PHP 8.3+ or Pest 2.0+ certification. Pin the version you tested against, audit the transitive dependency tree (especially phpunit/phpunit versions), and keep the adapter isolated so you can migrate away if Infection folds this layer back into its monorepo.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator