-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FR] Handle expectDeprecation*() et al methods removed by PHPUnit 10 #186
Comments
@hellofromtonya I get where you are coming from, but as you already mentioned: this library makes the tests forward compatible - i.e. it polyfills new PHPUnit functionality -, not backward compatible (polyfill removed functionality). With that in mind, as PHPUnit 11 will introduce the That still doesn't address the removed I have no objection to linking to another PHPUnit add-on package which does polyfill it (from the FAQ) or to a tutorial which shows users how to do it. Or even to a comment in this thread with an explanation. But I don't think the PHPUnit Polyfills package should polyfill it in 2.x and for 3.x, only the (new) functionality added in PHPUnit 11 should be polyfilled IMO. |
Custom Approach TutorialThe following custom approach tutorial is a Proof of Concept (PoC) to help guide developers who need to these expects in their project. This tutorial will cover how to polyfill There are multiple ways to polyfill these expectations. The custom approach, presented here, uses a custom extension and the events system. This custom approach:
The following sections provides guidance for you to adapt this custom approach for your application. Disclaimer: As a PoC, this is not guaranteed or warranted. Developers will need to tailor it to their specific needs. OverviewThe building blocks of this custom polyfill are:
This approach will use the following naming:
Basic call stackWhen a test invokes one of the polyfills, for example:
When a deprecation is triggered,
When the test is prepared for execution:
Just before the test completes:
Custom polyfills traitThis custom trait polyfills each of the expected deprecations. You'll drop into each test class that tests for deprecations. Please note, following example is heavily influenced by and in many cases copied from PHPUnit 11's implementation for its handling. When using, ensure to give proper credit. Code for ExpectDeprecation polyfills trait<?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Event\Code\ComparisonFailureBuilder;
use PHPUnit\Event\Code\ThrowableBuilder;
use PHPUnit\Event\Facade as EventFacade;
use PHPUnit\Framework\ExpectationFailedException;
trait ExpectDeprecation
{
protected $expectedDeprecations = 0;
protected $expectedDeprecationMessage = [];
protected $expectedDeprecationMessageRegularExpression = [];
final protected function expectDeprecation(): void
{
$this->setUpExpectDeprecation();
$this->expectedDeprecations++;
}
final protected function expectDeprecationMessage($expectedDeprecationMessage): void
{
$this->setUpExpectDeprecation();
$this->expectedDeprecationMessage[$expectedDeprecationMessage] = false;
}
final protected function expectDeprecationMessage($expectedDeprecationMessage): void
{
$this->setUpExpectDeprecation();
$this->expectedDeprecationMessage[$expectedDeprecationMessage] = false;
}
final protected function expectDeprecationMessageMatches($expectedDeprecationMessageRegularExpression): void
{
$this->setUpExpectDeprecation();
$this->expectedDeprecationMessageRegularExpression[] = $expectedDeprecationMessageRegularExpression;
}
final protected function expectDeprecationMessageMatches($expectedDeprecationMessageRegularExpression): void
{
$this->setUpExpectDeprecation();
$this->expectedDeprecationMessageRegularExpression[] = $expectedDeprecationMessageRegularExpression;
}
private function setUpExpectDeprecation(): void
{
DeprecationExtension::setTestInstance($this);
$this->addToAssertionCount(1);
}
final public function verifyDeprecationExpectations(): void
{
if ($this->expectedDeprecations > 0)
{
if (!empty( DeprecationExtension::deprecations() ))
{
static::assertThat(true, static::isTrue());
}
else
{
$this->failExpectDeprecationTest('Expected deprecation was not triggered');
}
}
foreach (array_keys($this->expectedDeprecationMessage) as $deprecationExpectation)
{
if (DeprecationExtension::triggered($deprecationExpectation))
{
static::assertThat(true, static::isTrue());
}
else
{
$this->failExpectDeprecationTest(
sprintf(
'Expected deprecation with message "%s" was not triggered',
$deprecationExpectation,
)
);
}
}
}
public function resetExpectedDeprecationMessages(): void
{
$this->expectedDeprecationMessage = [];
$this->expectedDeprecationMessageRegularExpression = [];
}
private function failExpectDeprecationTest($message): void
{
$e = new ExpectationFailedException($message);
EventFacade::emitter()->testFailed(
$this->valueObjectForEvents(),
ThrowableBuilder::from($e),
ComparisonFailureBuilder::from($e),
);
}
} Custom extensionRefer to "Implementing an extension" in the PHPUnit 10 documentation. The extension extends the Test Runner. It implements <?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Runner\Extension\Extension as PHPUnitExtension;
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
class DeprecationExtension implements PHPUnitExtension
{
public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
{
$facade->registerSubscribers(
new DeprecationSubscriber(),
new VerifyDeprecationExpectationsSubscriber(),
new ResetSubscriber()
);
}
} For simplicity of documenting this example, it uses a singleton approach for the subscribers and polyfill to interact with it. private static $instance;
public static function instance(): self
{
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
} To bind the current test to the extension: private $testInstance;
public static function setTestInstance($testInstance): void
{
self::instance()->testInstance = $testInstance;
} While a Collector could be used, for this example, the collection of actual deprecations is within the extension: protected array $actualDeprecations = [];
public static function collect(DeprecationTriggered $event): void
{
$message = $event->message();
self::instance()->actualDeprecations[$message] = true;
} The public static function verify(): void
{
self::instance()->testInstance->verifyDeprecationExpectations();
}
public static function triggered($message): bool
{
return isset(self::instance()->actualDeprecations[$message]);
} Code for DeprecationExtension custom extension<?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Runner\Extension\Extension as PHPUnitExtension;
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\Event\Test\DeprecationTriggered;
class DeprecationExtension implements PHPUnitExtension
{
private static $instance;
protected array $actualDeprecations = [];
private $testInstance;
public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
{
$facade->registerSubscribers(
new DeprecationSubscriber(),
new VerifyDeprecationExpectationsSubscriber(),
new ResetSubscriber()
);
}
public static function instance(): self
{
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public static function triggered($message): bool
{
return isset(self::instance()->actualDeprecations[$message]);
}
public static function collect(DeprecationTriggered $event): void
{
$message = $event->message();
self::instance()->actualDeprecations[$message] = true;
}
public static function setTestInstance($testInstance): void
{
self::instance()->testInstance = $testInstance;
}
public static function verify(): void
{
self::instance()->testInstance->verifyDeprecationExpectations();
}
public static function reset(): void
{
$instance = self::instance();
$instance->actualDeprecations = [];
if ($instance->testInstance)
{
$instance->testInstance->resetExpectedDeprecationMessages();
}
}
} Registering the extensionTo make PHPUnit aware of your extension, you'll need need to register it in the PHPUnit XML configuration file. For this custom extension example, the following is added: <extensions>
<bootstrap class="Vendor\YourPackage\Deprecations\DeprecationExtension"/>
</extensions> If your extension is shared from a PHAR, please refer to the PHPUnit 10 documentation for how to register it. Deprecations subscriber: Capturing your application's deprecationsThe event system includes several deprecation triggered events. You'll use the You'll add a custom subscriber that implements this interface, e.g. Code for DeprecationSubscriber<?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Event\Test\DeprecationTriggeredSubscriber;
use PHPUnit\Event\Test\DeprecationTriggered;
class DeprecationSubscriber implements DeprecationTriggeredSubscriber
{
public function notify(DeprecationTriggered $event): void
{
DeprecationExtension::collect($event);
}
} Verify deprecation expectations subscriberThis subscriber invokes the verify deprecation expectations process. You'll use the Code for VerifyDeprecationExpectationsSubscriber<?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Event\Test\AfterTestMethodFinishedSubscriber;
use PHPUnit\Event\Test\AfterTestMethodFinished;
class VerifyDeprecationExpectationsSubscriber implements AfterTestMethodFinishedSubscriber
{
public function notify(AfterTestMethodFinished $event): void
{
DeprecationExtension::verify($event);
}
} Reset subscriber: reset between testsThis subscriber resets between tests. You'll use the Code for ResetSubscriber<?php
namespace Vendor\YourPackage\Deprecations;
use PHPUnit\Event\Test\Prepared;
use PHPUnit\Event\Test\PreparedSubscriber;
class ResetSubscriber implements PreparedSubscriber
{
public function notify(Prepared $event): void
{
DeprecationExtension::reset();
}
} |
Thanks @hellofromtonya That looks amazing! |
PHPUnit 10 removes the following methods:
There is a valid use case for testing your application's triggered deprecations (as well as notices, warnings, etc.).
For deprecations, PHPUnit 11 introduces
expectUserDeprecation*()
methods, which are similar though not exactly the same asexpectDeprecation*()
removed in PHPUnit 10.With PHPUnit 10 removal, polyfilling these removed methods does not fit within this project's forward compatible approach. That said, knowing the impacts and likely needs of projects, let's explore:
Ref: https://github.com/Yoast/PHPUnit-Polyfills/tree/1.x?tab=readme-ov-file#phpunit--840-yoastphpunitpolyfillspolyfillsexpectphpexception
The text was updated successfully, but these errors were encountered: