π€ AI-generated β created with Claude Code, reviewed by @Tabrisrp. Surfaced while migrating wp-media/mcp-oauth to wp-media/phpunit v3.2. Contributes to #30.
Problem
Every consumer plugin must ship duplicate bootstrap shims β Tests/Unit/init-tests.php and Tests/Integration/init-tests.php β that each define two constants and delegate to the library bootstrap. This per-plugin boilerplate runs counter to the #30 goal of reducing maintenance across plugins.
Why the shim is currently required
The library bootstraps via src/Unit/bootstrap.php and src/Integration/bootstrap.php. These files use the constants WPMEDIA_PHPUNIT_ROOT_DIR and WPMEDIA_PHPUNIT_ROOT_TEST_DIR but do not define them β they assume the caller defined them first.
- When run through the
wpmedia-phpunit binary, BootstrapManager::setupConstants() defines these constants in the parent process.
- However, for
@runInSeparateProcess tests, PHPUnit re-runs the phpunit-config bootstrap file standalone in a child process where the binary never runs.
- If the phpunit config's
bootstrap= points directly at src/Unit/bootstrap.php, the child fatals with Undefined constant "WPMEDIA_PHPUNIT_ROOT_DIR".
- Consumers therefore keep an
init-tests.php shim purely to define those constants (guarded) before requiring the library bootstrap, so it works in both the binary (parent) and isolated-child contexts.
A typical consumer shim (from mcp-oauth):
if ( ! defined( 'WPMEDIA_PHPUNIT_ROOT_DIR' ) ) {
define( 'WPMEDIA_PHPUNIT_ROOT_DIR', dirname( dirname( __DIR__ ) ) );
}
if ( ! defined( 'WPMEDIA_PHPUNIT_ROOT_TEST_DIR' ) ) {
define( 'WPMEDIA_PHPUNIT_ROOT_TEST_DIR', __DIR__ );
}
require_once WPMEDIA_PHPUNIT_ROOT_DIR . '/vendor/wp-media/phpunit/src/Unit/bootstrap.php';
Proposed solution
Make src/Unit/bootstrap.php and src/Integration/bootstrap.php self-sufficient by defining the constants when absent, reusing existing logic:
if ( ! defined( 'WPMEDIA_PHPUNIT_ROOT_DIR' ) ) {
\WPMedia\PHPUnit\BootstrapManager::setupConstants( 'unit' ); // 'integration' in the Integration bootstrap
}
BootstrapManager::setupConstants() already derives the project root from __DIR__ and defaults the test dir to Tests/ + ucfirst(suite), so no consumer input is required.
Result: a consumer points their phpunit config at bootstrap="β¦/vendor/wp-media/phpunit/src/Unit/bootstrap.php" (or relies on the bundled config) and deletes both init-tests.php files. Isolated children work because the bootstrap now self-derives the constants when re-run standalone.
Secondary: phpunit.xml.dist duplication
BootstrapManager::getPhpunitXml() already falls back to the bundled src/{Unit,Integration}/phpunit.xml.dist when a consumer has none β good. But the bundled config can't express a consumer's project-specific code-coverage source dir (inc/ vs src/ vs includes/), and PHPUnit 9 has no native config inheritance, so consumers keep a full phpunit.xml.dist mainly to scope <coverage>.
Suggestion: let the bundled config's <coverage><include> read the source directory from an env var (e.g. WPMEDIA_PHPUNIT_SRC) or composer extra, so conventional-layout projects need no phpunit.xml.dist at all. (Config inheritance itself is a PHPUnit limitation, not something the library can fully solve.)
Impact
Combined with the self-locating bootstrap, a consumer's per-suite footprint shrinks to just its genuinely project-specific Tests/{Unit,Integration}/bootstrap.php (which wires the plugin under test) β directly advancing the #30 goal of reducing per-plugin maintenance.
References
Contributes to #30.
Problem
Every consumer plugin must ship duplicate bootstrap shims β
Tests/Unit/init-tests.phpandTests/Integration/init-tests.phpβ that each define two constants and delegate to the library bootstrap. This per-plugin boilerplate runs counter to the #30 goal of reducing maintenance across plugins.Why the shim is currently required
The library bootstraps via
src/Unit/bootstrap.phpandsrc/Integration/bootstrap.php. These files use the constantsWPMEDIA_PHPUNIT_ROOT_DIRandWPMEDIA_PHPUNIT_ROOT_TEST_DIRbut do not define them β they assume the caller defined them first.wpmedia-phpunitbinary,BootstrapManager::setupConstants()defines these constants in the parent process.@runInSeparateProcesstests, PHPUnit re-runs the phpunit-configbootstrapfile standalone in a child process where the binary never runs.bootstrap=points directly atsrc/Unit/bootstrap.php, the child fatals withUndefined constant "WPMEDIA_PHPUNIT_ROOT_DIR".init-tests.phpshim purely to define those constants (guarded) before requiring the library bootstrap, so it works in both the binary (parent) and isolated-child contexts.A typical consumer shim (from mcp-oauth):
Proposed solution
Make
src/Unit/bootstrap.phpandsrc/Integration/bootstrap.phpself-sufficient by defining the constants when absent, reusing existing logic:BootstrapManager::setupConstants()already derives the project root from__DIR__and defaults the test dir toTests/+ ucfirst(suite), so no consumer input is required.Result: a consumer points their phpunit config at
bootstrap="β¦/vendor/wp-media/phpunit/src/Unit/bootstrap.php"(or relies on the bundled config) and deletes bothinit-tests.phpfiles. Isolated children work because the bootstrap now self-derives the constants when re-run standalone.Secondary:
phpunit.xml.distduplicationBootstrapManager::getPhpunitXml()already falls back to the bundledsrc/{Unit,Integration}/phpunit.xml.distwhen a consumer has none β good. But the bundled config can't express a consumer's project-specific code-coverage source dir (inc/vssrc/vsincludes/), and PHPUnit 9 has no native config inheritance, so consumers keep a fullphpunit.xml.distmainly to scope<coverage>.Suggestion: let the bundled config's
<coverage><include>read the source directory from an env var (e.g.WPMEDIA_PHPUNIT_SRC) or composerextra, so conventional-layout projects need nophpunit.xml.distat all. (Config inheritance itself is a PHPUnit limitation, not something the library can fully solve.)Impact
Combined with the self-locating bootstrap, a consumer's per-suite footprint shrinks to just its genuinely project-specific
Tests/{Unit,Integration}/bootstrap.php(which wires the plugin under test) β directly advancing the #30 goal of reducing per-plugin maintenance.References
Contributes to #30.