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

Phpunit Legacy Adapter Laravel Package

sanmai/phpunit-legacy-adapter

Compatibility adapter for running legacy PHPUnit test suites on newer PHPUnit versions. Helps bridge API changes, keep older tests passing, and smooth migrations without rewriting everything. Suitable for maintaining long-lived PHP projects with outdated test setups.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package:

    composer require --dev sanmai/phpunit-legacy-adapter:"^6.4 || ^8.2.1"
    
    • Use ^6.4 for PHPUnit 4/5/6 (PHP 5.3–7.4).
    • Use ^8.2.1 for PHPUnit 7/8/9/10/11/12/13 (PHP 7.1–8.1).
  2. Update Test Classes: Replace the base class in your test files:

    - class MyTest extends \PHPUnit\Framework\TestCase
    + class MyTest extends \LegacyPHPUnit\TestCase
    
  3. Replace Template Methods: Update method signatures to use the do* variants:

    - protected function setUp(): void
    + protected function doSetUp()
    
  4. Run Tests: Execute PHPUnit as usual. The adapter handles the rest, bypassing void return type requirements.

First Use Case

Scenario: You have a legacy Laravel project running PHP 7.0 with PHPUnit 8.x, but tests fail due to void return type declarations in setUp()/tearDown(). Solution:

  • Install the adapter.
  • Update phpunit.xml to use PHPUnit 8.x.
  • Replace setUp(): void with doSetUp() in all test classes.
  • Run tests—legacy PHP compatibility is restored without rewriting tests.

Implementation Patterns

Core Workflow

  1. Adapter Initialization: The package hooks into PHPUnit’s test lifecycle via the \LegacyPHPUnit\TestCase base class. No additional configuration is needed beyond extending this class.

  2. Method Replacement: The adapter dynamically intercepts calls to legacy template methods (e.g., setUp()) and delegates them to doSetUp(). This avoids PHP 7.0’s inability to declare void return types.

  3. Assertion Polyfills: While the adapter doesn’t handle assertion changes, you can extend \LegacyPHPUnit\TestCase to add custom __call() logic for missing assertions (e.g., assertStringContainsString):

    class MyTest extends \LegacyPHPUnit\TestCase
    {
        public function __call($method, $args)
        {
            if ($method === 'assertStringContainsString') {
                return $this->assertContains($args[0], $args[1]);
            }
            throw new \BadMethodCallException("Method {$method} not found.");
        }
    }
    
  4. Hybrid Test Suites: Use the adapter selectively in legacy test files while keeping modern tests as-is. Example:

    // LegacyTest.php
    class LegacyTest extends \LegacyPHPUnit\TestCase { ... }
    
    // ModernTest.php
    class ModernTest extends \PHPUnit\Framework\TestCase { ... }
    

Integration Tips

  • Laravel-Specific:

    • Place the adapter in tests/TestCase.php to avoid per-file updates:
      use LegacyPHPUnit\TestCase as BaseTestCase;
      
      abstract class TestCase extends BaseTestCase { ... }
      
    • Extend TestCase in all legacy test files.
  • CI/CD Pipelines:

    • Ensure your CI environment uses the same PHPUnit version as your local setup (e.g., phpunit/phpunit:^8.2).
    • Cache Composer dependencies to avoid reinstallation overhead.
  • Static Analysis:

    • Exclude adapter-related files from tools like PHPStan/Psalm:
      # phpstan.neon
      excludes:
        - vendor/sanmai/phpunit-legacy-adapter/
      
  • Parallel Testing:

    • The adapter is thread-safe for parallel test runners (e.g., php-parallel-lint), but verify with your specific setup.

Gotchas and Tips

Pitfalls

  1. PHPUnit Version Mismatch:

    • The adapter only works with PHPUnit 6.4 or 8.2.1. Using newer versions (e.g., PHPUnit 9+) may break compatibility.
    • Fix: Pin PHPUnit to the supported versions in composer.json:
      "require-dev": {
          "phpunit/phpunit": "^8.2.1"
      }
      
  2. Custom Test Classes:

    • If your test classes use __call(), __callStatic(), or other magic methods, the adapter may interfere.
    • Fix: Avoid overriding these methods or ensure they delegate to the parent class.
  3. Static Analysis Warnings:

    • Tools like PHPStan may flag "undefined" return types for doSetUp()/doTearDown().
    • Fix: Add type hints manually or exclude the adapter from analysis.
  4. Assertion Gaps:

    • The adapter doesn’t polyfill all assertion changes (e.g., assertSame() behavior differs across PHPUnit versions).
    • Fix: Use a dedicated polyfill library (e.g., yoast/phpunit-polyfills) alongside the adapter.
  5. Performance Overhead:

    • Runtime method interception adds minimal overhead. For large test suites (>10,000 tests), measure impact with:
      phpunit --coverage-text --colors=never | grep "Time"
      

Debugging Tips

  • Verify Adapter Activation: Add a debug method to confirm the adapter is loaded:

    class MyTest extends \LegacyPHPUnit\TestCase
    {
        public function testAdapterLoaded()
        {
            $this->assertTrue(method_exists($this, 'doSetUp'));
        }
    }
    
  • Check Method Calls: Use Xdebug or strace to trace method calls if tests behave unexpectedly:

    strace -f -e trace=execve,open phpunit MyTest
    
  • Isolate Issues: Test a single legacy file first to rule out conflicts with other packages.

Extension Points

  1. Custom Polyfills: Extend \LegacyPHPUnit\TestCase to add missing assertions:

    class CustomTestCase extends \LegacyPHPUnit\TestCase
    {
        public function assertIsBool($value)
        {
            $this->assertTrue(is_bool($value));
        }
    }
    
  2. Global Assertion Replacement: Use a trait to centralize polyfills:

    trait LegacyAssertions
    {
        public function assertStringContainsString($needle, $haystack)
        {
            $this->assertContains($needle, $haystack);
        }
    }
    
    class MyTest extends \LegacyPHPUnit\TestCase
    {
        use LegacyAssertions;
    }
    
  3. Bootstrap Integration: Load the adapter globally via phpunit.xml:

    <php>
        <autoload>
            <classmap prefix="LegacyPHPUnit"/>
        </autoload>
    </php>
    

Laravel-Specific Quirks

  • Service Provider Conflicts: If using Laravel’s PHPUnitServiceProvider, ensure it doesn’t override the test case base class. Override the provider’s register() method:

    public function register()
    {
        $this->app->singleton('testing', function () {
            return new TestCase();
        });
    }
    
  • Artisan Test Runner: The adapter works with php artisan test, but ensure your phpunit.xml points to the correct PHPUnit version:

    <phpunit bootstrap="vendor/autoload.php">
        <php>
            <ini name="memory_limit" value="1024M"/>
        </php>
    </phpunit>
    
  • Database Transactions: Legacy tests using DatabaseTransactions may interact poorly with the adapter’s lifecycle. Test thoroughly with:

    use Illuminate\Foundation\Testing\DatabaseTransactions;
    
    class MyTest extends \LegacyPHPUnit\TestCase
    {
        use DatabaseTransactions;
    }
    
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.
aimeos/prisma
besmartand-pro/php-quality-config
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor
spatie/laravel-javascript-views