[PhpUnitBridge] Add the ability to expect a deprecation inside a test
This commit is contained in:
parent
9eb7cb1b7b
commit
a3a928050d
@ -6,6 +6,7 @@ CHANGELOG
|
||||
|
||||
* ignore verbosity settings when the build fails because of deprecations
|
||||
* added per-group verbosity
|
||||
* added `ExpectDeprecationTrait` to be able to define an expected deprecation from inside a test
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
31
src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php
Normal file
31
src/Symfony/Bridge/PhpUnit/ExpectDeprecationTrait.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PhpUnit;
|
||||
|
||||
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait;
|
||||
|
||||
trait ExpectDeprecationTrait
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function expectDeprecation($message)
|
||||
{
|
||||
if (!SymfonyTestsListenerTrait::$previousErrorHandler) {
|
||||
SymfonyTestsListenerTrait::$previousErrorHandler = set_error_handler([SymfonyTestsListenerTrait::class, 'handleError']);
|
||||
}
|
||||
|
||||
SymfonyTestsListenerTrait::$expectedDeprecations[] = $message;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\RiskyTestError;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Runner\BaseTestRunner;
|
||||
@ -20,6 +21,7 @@ use PHPUnit\Util\Blacklist;
|
||||
use PHPUnit\Util\Test;
|
||||
use Symfony\Bridge\PhpUnit\ClockMock;
|
||||
use Symfony\Bridge\PhpUnit\DnsMock;
|
||||
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
|
||||
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
|
||||
@ -32,16 +34,16 @@ use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
*/
|
||||
class SymfonyTestsListenerTrait
|
||||
{
|
||||
public static $expectedDeprecations = [];
|
||||
public static $previousErrorHandler;
|
||||
private static $gatheredDeprecations = [];
|
||||
private static $globallyEnabled = false;
|
||||
private $state = -1;
|
||||
private $skippedFile = false;
|
||||
private $wasSkipped = [];
|
||||
private $isSkipped = [];
|
||||
private $expectedDeprecations = [];
|
||||
private $gatheredDeprecations = [];
|
||||
private $previousErrorHandler;
|
||||
private $error;
|
||||
private $runsInSeparateProcess = false;
|
||||
private $checkNumAssertions = false;
|
||||
|
||||
/**
|
||||
* @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive)
|
||||
@ -220,15 +222,17 @@ class SymfonyTestsListenerTrait
|
||||
if (isset($annotations['class']['expectedDeprecation'])) {
|
||||
$test->getTestResultObject()->addError($test, new AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0);
|
||||
}
|
||||
if (isset($annotations['method']['expectedDeprecation']) || $this->checkNumAssertions = \in_array(ExpectDeprecationTrait::class, class_uses($test), true)) {
|
||||
if (isset($annotations['method']['expectedDeprecation'])) {
|
||||
if (!\in_array('legacy', $groups, true)) {
|
||||
$this->error = new AssertionFailedError('Only tests with the `@group legacy` annotation can have `@expectedDeprecation`.');
|
||||
self::$expectedDeprecations = $annotations['method']['expectedDeprecation'];
|
||||
self::$previousErrorHandler = set_error_handler([self::class, 'handleError']);
|
||||
}
|
||||
|
||||
if ($this->checkNumAssertions) {
|
||||
$this->checkNumAssertions = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything() && !$test->doesNotPerformAssertions();
|
||||
}
|
||||
|
||||
$test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
|
||||
|
||||
$this->expectedDeprecations = $annotations['method']['expectedDeprecation'];
|
||||
$this->previousErrorHandler = set_error_handler([$this, 'handleError']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,9 +246,12 @@ class SymfonyTestsListenerTrait
|
||||
$className = \get_class($test);
|
||||
$groups = Test::getGroups($className, $test->getName(false));
|
||||
|
||||
if ($errored = null !== $this->error) {
|
||||
$test->getTestResultObject()->addError($test, $this->error, 0);
|
||||
$this->error = null;
|
||||
if ($this->checkNumAssertions) {
|
||||
if (!self::$expectedDeprecations && !$test->getNumAssertions()) {
|
||||
$test->getTestResultObject()->addFailure($test, new RiskyTestError('This test did not perform any assertions'), $time);
|
||||
}
|
||||
|
||||
$this->checkNumAssertions = false;
|
||||
}
|
||||
|
||||
if ($this->runsInSeparateProcess) {
|
||||
@ -263,24 +270,26 @@ class SymfonyTestsListenerTrait
|
||||
$this->runsInSeparateProcess = false;
|
||||
}
|
||||
|
||||
if ($this->expectedDeprecations) {
|
||||
if (self::$expectedDeprecations) {
|
||||
if (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE], true)) {
|
||||
$test->addToAssertionCount(\count($this->expectedDeprecations));
|
||||
$test->addToAssertionCount(\count(self::$expectedDeprecations));
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if (!$errored && !\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
|
||||
if (!\in_array('legacy', $groups, true)) {
|
||||
$test->getTestResultObject()->addError($test, new AssertionFailedError('Only tests with the `@group legacy` annotation can expect a deprecation.'), 0);
|
||||
} elseif (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
|
||||
try {
|
||||
$prefix = "@expectedDeprecation:\n";
|
||||
$test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", $this->expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", $this->gatheredDeprecations)."\n");
|
||||
$test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", self::$expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", self::$gatheredDeprecations)."\n");
|
||||
} catch (AssertionFailedError $e) {
|
||||
$test->getTestResultObject()->addFailure($test, $e, $time);
|
||||
}
|
||||
}
|
||||
|
||||
$this->expectedDeprecations = $this->gatheredDeprecations = [];
|
||||
$this->previousErrorHandler = null;
|
||||
self::$expectedDeprecations = self::$gatheredDeprecations = [];
|
||||
self::$previousErrorHandler = null;
|
||||
}
|
||||
if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
|
||||
if (\in_array('time-sensitive', $groups, true)) {
|
||||
@ -292,10 +301,10 @@ class SymfonyTestsListenerTrait
|
||||
}
|
||||
}
|
||||
|
||||
public function handleError($type, $msg, $file, $line, $context = [])
|
||||
public static function handleError($type, $msg, $file, $line, $context = [])
|
||||
{
|
||||
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
|
||||
$h = $this->previousErrorHandler;
|
||||
$h = self::$previousErrorHandler;
|
||||
|
||||
return $h ? $h($type, $msg, $file, $line, $context) : false;
|
||||
}
|
||||
@ -308,7 +317,7 @@ class SymfonyTestsListenerTrait
|
||||
if (error_reporting()) {
|
||||
$msg = 'Unsilenced deprecation: '.$msg;
|
||||
}
|
||||
$this->gatheredDeprecations[] = $msg;
|
||||
self::$gatheredDeprecations[] = $msg;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PhpUnit\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
|
||||
|
||||
final class ExpectDeprecationTraitTest extends TestCase
|
||||
{
|
||||
use ExpectDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Do not remove this test in the next major version.
|
||||
*
|
||||
* @group legacy
|
||||
*/
|
||||
public function testOne()
|
||||
{
|
||||
$this->expectDeprecation('foo');
|
||||
@trigger_error('foo', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not remove this test in the next major version.
|
||||
*
|
||||
* @group legacy
|
||||
*/
|
||||
public function testMany()
|
||||
{
|
||||
$this->expectDeprecation('foo');
|
||||
$this->expectDeprecation('bar');
|
||||
@trigger_error('foo', E_USER_DEPRECATED);
|
||||
@trigger_error('bar', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not remove this test in the next major version.
|
||||
*
|
||||
* @group legacy
|
||||
*
|
||||
* @expectedDeprecation foo
|
||||
*/
|
||||
public function testOneWithAnnotation()
|
||||
{
|
||||
$this->expectDeprecation('bar');
|
||||
@trigger_error('foo', E_USER_DEPRECATED);
|
||||
@trigger_error('bar', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not remove this test in the next major version.
|
||||
*
|
||||
* @group legacy
|
||||
*
|
||||
* @expectedDeprecation foo
|
||||
* @expectedDeprecation bar
|
||||
*/
|
||||
public function testManyWithAnnotation()
|
||||
{
|
||||
$this->expectDeprecation('ccc');
|
||||
$this->expectDeprecation('fcy');
|
||||
@trigger_error('foo', E_USER_DEPRECATED);
|
||||
@trigger_error('bar', E_USER_DEPRECATED);
|
||||
@trigger_error('ccc', E_USER_DEPRECATED);
|
||||
@trigger_error('fcy', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user