automattic/phpunit-select-config
Small utility for PHPUnit projects that helps select or switch the PHPUnit configuration file to use when running tests. Handy for repos with multiple phpunit.xml variants (e.g., local vs CI) and scripts that need consistent config selection.
Install the Package:
composer require --dev automattic/phpunit-select-config
This adds the phpunit-select-config binary to vendor/bin/.
Create Versioned Config Files:
Name your PHPUnit configs with a versioned pattern (e.g., phpunit.8.xml.dist, phpunit.9.xml.dist, phpunit.10.xml.dist). The # in the filename pattern (e.g., phpunit.#.xml.dist) will be replaced by the detected PHPUnit major version.
First Usage:
Replace direct phpunit calls with:
vendor/bin/phpunit-select-config phpunit.#.xml.dist
Or via Composer:
composer exec -- phpunit-select-config phpunit.#.xml.dist
Laravel Integration:
Add a custom Artisan command (e.g., app/Console/Commands/RunVersionedTests.php) to wrap the package:
use Automattic\PHPUnitSelectConfig\Runner;
class RunVersionedTests extends Command {
protected $signature = 'test:versioned {config_pattern? : e.g., phpunit.#.xml.dist}';
public function handle() {
$pattern = $this->argument('config_pattern') ?? 'phpunit.#.xml.dist';
$runner = new Runner($pattern);
$runner->run();
}
}
Register the command in app/Console/Kernel.php:
protected $commands = [
Commands\RunVersionedTests::class,
];
Version-Based Testing: Use the package to automatically select configs based on the PHPUnit version installed in your environment (e.g., CI vs. local dev). Example:
# Runs phpunit.9.xml.dist if PHPUnit 9.x is detected
php artisan test:versioned phpunit.#.xml.dist
Environment-Specific Configs:
Extend the pattern to include environment-specific configs (e.g., phpunit.ci.#.xml.dist, phpunit.local.#.xml.dist). Override the pattern dynamically:
$env = app()->environment();
$pattern = "phpunit.{$env}.#.xml.dist";
$runner = new Runner($pattern);
Feature-Flagged Tests:
Use the package to toggle test suites via config files (e.g., phpunit.feature-x.#.xml.dist). Trigger via CLI:
php artisan test:versioned phpunit.feature-x.#.xml.dist
CI/CD Pipeline Integration:
Replace php artisan test with the versioned command in CI scripts. Example (GitHub Actions):
- name: Run Tests
run: php artisan test:versioned phpunit.ci.#.xml.dist
Local Development:
phpunit.local.#.xml.dist for local overrides (e.g., slower tests, debug configurations).composer exec -- phpunit-select-config phpunit.local.#.xml.dist
Parallel Test Execution:
Combine with Laravel’s --parallel flag:
php artisan test:versioned phpunit.#.xml.dist --parallel
Custom Test Groups:
Create configs like phpunit.unit.#.xml.dist and phpunit.integration.#.xml.dist, then run selectively:
php artisan test:versioned phpunit.unit.#.xml.dist
Laravel TestCase Bootstrapping:
Ensure your TestCase extends Illuminate\Foundation\Testing\TestCase and that the package’s config selection doesn’t interfere with Laravel’s service providers. If issues arise, override the createApplication method to enforce config selection:
public function createApplication() {
$config = $this->getTestConfig(); // Custom logic to fetch config
$runner = new Runner($config);
return $runner->createLaravelApplication();
}
Composer Scripts: Add a custom Composer script to simplify usage:
{
"scripts": {
"test": "php artisan test:versioned phpunit.#.xml.dist",
"test:unit": "php artisan test:versioned phpunit.unit.#.xml.dist"
}
}
Now run tests with:
composer test
Environment Variables:
Allow dynamic config selection via .env:
TEST_CONFIG_PATTERN=phpunit.ci.#.xml.dist
Update the Artisan command to read this:
$pattern = env('TEST_CONFIG_PATTERN', 'phpunit.#.xml.dist');
Fallback Configs:
Provide a default config in the pattern (e.g., phpunit.default.xml.dist) to handle cases where the versioned config doesn’t exist:
php artisan test:versioned phpunit.#.xml.dist --fallback=phpunit.default.xml.dist
Filename Pattern Mismatch:
# in the pattern must match the PHPUnit major version (e.g., phpunit.9.xml.dist for PHPUnit 9.x). If the file doesn’t exist, the package will fail silently or throw an error.if (!file_exists($selectedConfig)) {
$this->error("Config file not found: {$selectedConfig}");
return 1;
}
PHPUnit Version Detection:
phpunit --version. If PHPUnit isn’t in PATH or the version is unrecognized, it may default to an incorrect config.Laravel Service Provider Conflicts:
TestCase relies on service providers or bindings that aren’t loaded in the selected config, tests may fail unexpectedly.createApplication to load them dynamically.CI/CD Environment Issues:
composer.json or use a matrix strategy in CI to test multiple versions.Performance Overhead:
Verbose Output: Enable debug mode to see which config is being selected:
composer exec -- phpunit-select-config --verbose phpunit.#.xml.dist
Or extend your Artisan command:
$runner->setVerbose(true);
Logging: Add logging to track config selection:
Log::info("Selected config: {$selectedConfig}");
Dry Runs: Test config selection without running tests:
composer exec -- phpunit-select-config --dry-run phpunit.#.xml.dist
Case Sensitivity:
Filename patterns are case-sensitive. Ensure your configs match the pattern exactly (e.g., phpunit.8.xml.dist vs. phpunit.8.XML.dist).
Default Configs: The package doesn’t provide a built-in default config. Always specify a fallback or ensure the pattern matches an existing file.
Custom PHPUnit Arguments:
Arguments passed to phpunit-select-config are forwarded to PHPUnit, but some (e.g., --configuration) may conflict with the package’s logic.
--configuration manually; let the package handle it.Custom Config Selectors: Extend the package’s logic to support non-version-based selection (e.g., by environment, feature flag, or custom logic). Fork the package or create a wrapper:
class CustomConfigSelector {
public function selectConfig(string $pattern): string {
$version = $this->detectPhpUnitVersion();
$env = app()->environment();
return str_replace('#', $version, str_replace('.', ".{$env}.", $pattern));
}
}
Pre/Post-Execution Hooks: Add hooks to validate configs or modify the test environment before execution:
$runner->addPreHook(function ($config) {
if (!str_contains($config, 'coverage')) {
putenv('X
How can I help you explore Laravel packages today?