From 6b28becc2e6ed41281d18ee8074f53c2e5cd693f Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 21:52:10 -0800 Subject: [PATCH 1/6] Add `::setStrictModeForTest()` function --- php/WP_Mock.php | 81 ++++++++++++++++++++++++++++++- tests/Integration/WP_MockTest.php | 55 +++++++++++++++++++++ tests/Unit/WP_MockTest.php | 60 +++++++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 49ef1f3..920b155 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -32,6 +32,19 @@ class WP_Mock protected static $__strict_mode = false; + /** + * Check was strict mode set individually for this test. + * + * Using an associative array to hold test-method-string:is-enabled-bool. + * + * @used-by self::setStrictModeForTest() + * @used-by self::isStrictModeForTest() + * @see self::strictMode() + * + * @var array + */ + protected static $__strict_mode_for_individual_test = []; + /** @var DeprecatedMethodListener */ protected static $deprecatedMethodListener; @@ -57,7 +70,7 @@ public static function usingPatchwork() */ public static function strictMode() { - return (bool) self::$__strict_mode; + return self::isStrictModeForTest() ?? (bool) self::$__strict_mode; } /** @@ -70,6 +83,72 @@ public static function activateStrictMode() } } + /** + * Sets strict mode on or off at runtime for an individual test. + * + * Records the config/preference for the individual test. Later this will be preferred over the default. + * + * @param bool $enabled + * @throws Exception when the test case name cannot be determined. + */ + public static function setStrictModeForTest(bool $enabled = true): void + { + $currentTestName = self::getCurrentlyRunningTestName(); + if(is_null($currentTestName)){ + throw new Exception('Failed to determine current test name'); + } + self::$__strict_mode_for_individual_test = [$currentTestName => $enabled,]; + } + + /** + * Check was strict mode configured individually for this test case. + * + * @see self::setStrictModeForTest() + * @see self::$__strict_mode_for_individual_test + * + * @return ?bool `null` when not set, boolean preference when set. + */ + protected static function isStrictModeForTest(): ?bool { + if( empty( self::$__strict_mode_for_individual_test ) ) { + return null; + } + + $currentTestName = self::getCurrentlyRunningTestName(); + + if(!is_null($currentTestName) && isset(self::$__strict_mode_for_individual_test[$currentTestName])) { + return self::$__strict_mode_for_individual_test[$currentTestName]; + } + + // Reset the array since it is only relevant for the current test case run. + self::$__strict_mode_for_individual_test = []; + + return null; + } + + /** + * Perform a backtrace to determine the currently running test. + * + * @return ?string of `class-string::method` + */ + protected static function getCurrentlyRunningTestName(): ?string { + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + /** @var array{class?:string, function?:string} $trace */ + foreach ($backtrace as $trace) { + if (isset($trace['class']) && isset($trace['function'])) { + // Check if this is a PHPUnit test class + if (is_subclass_of($trace['class'], \PHPUnit\Framework\TestCase::class)) { + // Test method names start with 'test' or have @test annotation + if (strpos($trace['function'], 'test') === 0) { + return $trace['class'] . '::' . $trace['function']; + } + } + } + } + + return null; + } + /** * Bootstraps WP_Mock. * diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index 3af7bb3..e53bee9 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -335,4 +335,59 @@ public function testCanExpectHooksNotAdded() : void $this->assertConditionsMet(); } + + /** + * @covers \WP_Mock::setStrictModeForTest() + * + * @return void + * @throws Exception + */ + public function testCanDisableStrictModeForLegacyCode() : void + { + // Set default ala `WP_Mock::activateStrictMode()`. + $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, true ); + + // Temporarily disable strict mode to test legacy code + WP_Mock::setStrictModeForTest(false); + + // This would normally fail in strict mode, but should pass now + add_action('legacy_action', 'legacy_callback'); + + $this->assertTrue(true); + } + + /** + * @covers \WP_Mock::setStrictModeForTest() + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * + * @return void + * @throws Exception|ExpectationFailedException + */ + public function testCanEnableStrictModeForSpecificTest() : void + { + /** + * Set default ala `WP_Mock::activateStrictMode()`, `false`. + * @see \WP_Mock::$__strict_mode + */ + $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, false ); + + $this->assertFalse(WP_Mock::strictMode()); + + // Enable strict mode for this specific test + WP_Mock::setStrictModeForTest(); + $this->assertTrue(WP_Mock::strictMode()); + + // This should throw an exception because we haven't set expectations + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('No handler found for function unmocked_function'); + + // Call an unmocked function + WP_Mock\Functions\Handler::handleFunction('unmocked_function'); + } } diff --git a/tests/Unit/WP_MockTest.php b/tests/Unit/WP_MockTest.php index 82f34c0..edc7b63 100644 --- a/tests/Unit/WP_MockTest.php +++ b/tests/Unit/WP_MockTest.php @@ -68,6 +68,66 @@ public function testActivateStrictModeDoesNotWorkAfterBootstrap(): void $this->assertFalse(WP_Mock::strictMode()); } + /** + * @covers \WP_Mock::setStrictModeForTest() + * + * @return void + * @throws \Exception + */ + public function testSetStrictModeForTestCanDisableStrictMode(): void + { + // Set default ala `WP_Mock::activateStrictMode()`. + $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, true ); + + + WP_Mock::setStrictModeForTest(false); + + $this->assertFalse(WP_Mock::strictMode()); + } + + /** + * @covers \WP_Mock::setStrictModeForTest() + * + * @return void + * @throws \Exception + */ + public function testSetStrictModeForTestCanEnableStrictMode(): void + { + // Set default ala `WP_Mock::activateStrictMode()`, but `false`. + $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, false ); + + WP_Mock::setStrictModeForTest(); + + $this->assertTrue(WP_Mock::strictMode()); + } + + /** + * @covers \WP_Mock::setStrictModeForTest() + * + * @return void + * @throws ExpectationFailedException|InvalidArgumentException + */ + public function testPreviousSetStrictModeForTestIsNotRelevant(): void + { + // Set default ala `WP_Mock::activateStrictMode()`. + $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, true ); + + // Set individual test configuration for another tests. + $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode_for_individual_test' ); + $property->setAccessible( true ); + $property->setValue( null, [ + 'WP_Mock\Tests\Unit\WP_MockTest::testSetStrictModeForTestCanDisableStrictMode' => false + ]); + + $this->assertTrue(WP_Mock::strictMode()); + } + /** * @covers \WP_Mock::userFunction() * From d1dc707dbb46de3082bd774754de1e48d45e87bd Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 22:24:55 -0800 Subject: [PATCH 2/6] Reset strict mode before tests --- tests/Integration/WP_MockTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index e53bee9..b9ff343 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -44,6 +44,11 @@ class WP_MockTest extends WP_MockTestCase */ protected function setUp(): void { + // Reset to default strict-mode after tests that manipulate this value. + $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, false ); + if (! $this->isInIsolation()) { WP_Mock::setUp(); } From 567d315258faf917f624d828d437dd5e50132a47 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 22:26:01 -0800 Subject: [PATCH 3/6] Reset strict mode _after_tests --- tests/Integration/WP_MockTest.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index b9ff343..3875d8c 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -44,11 +44,6 @@ class WP_MockTest extends WP_MockTestCase */ protected function setUp(): void { - // Reset to default strict-mode after tests that manipulate this value. - $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); - $property->setAccessible( true ); - $property->setValue( null, false ); - if (! $this->isInIsolation()) { WP_Mock::setUp(); } @@ -56,6 +51,16 @@ protected function setUp(): void require_once(dirname(__DIR__).'/Mocks/Functions.php'); } + protected function tearDown(): void + { + parent::tearDown(); + + // Reset to default strict-mode after tests that manipulate this value. + $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); + $property->setAccessible( true ); + $property->setValue( null, false ); + } + /** * @covers \WP_Mock::bootstrap() * @covers \WP_Mock\Functions::__construct() From 4de613bcf7c434af8b0d26b94e7429405c257116 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 22:28:43 -0800 Subject: [PATCH 4/6] Improve PhpDoc description --- php/WP_Mock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 920b155..1f24249 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -33,9 +33,9 @@ class WP_Mock protected static $__strict_mode = false; /** - * Check was strict mode set individually for this test. + * A record if strict mode was set individually for this test. * - * Using an associative array to hold test-method-string:is-enabled-bool. + * Uses an associative array containing both method and setting as test-method-string:is-enabled-bool. * * @used-by self::setStrictModeForTest() * @used-by self::isStrictModeForTest() From 43c870d7012b63228fc0b4f80e7bfd1c6b6d4f23 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 22:39:07 -0800 Subject: [PATCH 5/6] Check PHP version before 8.5 deprecated `ReflectionProperty::setAccessible()` --- tests/Integration/WP_MockTest.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index 3875d8c..befed38 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -57,7 +57,10 @@ protected function tearDown(): void // Reset to default strict-mode after tests that manipulate this value. $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); - $property->setAccessible( true ); + // "Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect". + if(!version_compare(PHP_VERSION, '8.5', '>=')) { + $property->setAccessible( true ); + } $property->setValue( null, false ); } @@ -356,7 +359,9 @@ public function testCanDisableStrictModeForLegacyCode() : void { // Set default ala `WP_Mock::activateStrictMode()`. $property = new \ReflectionProperty( WP_Mock::class, '__strict_mode' ); - $property->setAccessible( true ); + if(!version_compare(PHP_VERSION, '8.5', '>=')) { + $property->setAccessible( true ); + } $property->setValue( null, true ); // Temporarily disable strict mode to test legacy code @@ -384,7 +389,9 @@ public function testCanEnableStrictModeForSpecificTest() : void * @see \WP_Mock::$__strict_mode */ $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); - $property->setAccessible( true ); + if(!version_compare(PHP_VERSION, '8.5', '>=')) { + $property->setAccessible( true ); + } $property->setValue( null, false ); $this->assertFalse(WP_Mock::strictMode()); From 03ed773b91e040a219f7945ab14553b05a0b644d Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 22:44:01 -0800 Subject: [PATCH 6/6] =?UTF-8?q?Move=20the=20`$=5F=5Fstrict=5Fmode`=20reset?= =?UTF-8?q?=20into=20setup=20=E2=80=93=20try=20fix=20PHP=208.3=20errror?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Integration/WP_MockTest.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index befed38..c441102 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -44,24 +44,23 @@ class WP_MockTest extends WP_MockTestCase */ protected function setUp(): void { - if (! $this->isInIsolation()) { - WP_Mock::setUp(); - } - - require_once(dirname(__DIR__).'/Mocks/Functions.php'); - } - - protected function tearDown(): void - { - parent::tearDown(); - - // Reset to default strict-mode after tests that manipulate this value. + /** + * Reset to default strict-mode after tests that manipulate this value. + * + * @see WP_Mock::$__strict_mode + */ $property = new \ReflectionProperty( \WP_Mock::class, '__strict_mode' ); // "Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect". if(!version_compare(PHP_VERSION, '8.5', '>=')) { $property->setAccessible( true ); } $property->setValue( null, false ); + + if (! $this->isInIsolation()) { + WP_Mock::setUp(); + } + + require_once(dirname(__DIR__).'/Mocks/Functions.php'); } /**