Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1d34c8c

Browse files
feature #35192 [PhpUnitBridge] Add the ability to expect a deprecation inside a test (fancyweb)
This PR was merged into the 5.1-dev branch. Discussion ---------- [PhpUnitBridge] Add the ability to expect a deprecation inside a test | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | todo Replaces #25757 Proposed implementation uses a dedicated trait for a better DX. Using `$this->expectDeprecation()` feels natural. Unfortunately it is not currently possible to test the cases that should produce errors or risky, so there are some things that are not testable here. I plan to propose another feature for the PhpUnitBridge to be able to test those kind of things. If it's accepted, we will then be able to strenghten the tests of this one. Commits ------- a3a9280 [PhpUnitBridge] Add the ability to expect a deprecation inside a test
2 parents bbeb49f + a3a9280 commit 1d34c8c

File tree

4 files changed

+139
-22
lines changed

4 files changed

+139
-22
lines changed

src/Symfony/Bridge/PhpUnit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* ignore verbosity settings when the build fails because of deprecations
88
* added per-group verbosity
9+
* added `ExpectDeprecationTrait` to be able to define an expected deprecation from inside a test
910

1011
5.0.0
1112
-----
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\PhpUnit;
13+
14+
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait;
15+
16+
trait ExpectDeprecationTrait
17+
{
18+
/**
19+
* @param string $message
20+
*
21+
* @return void
22+
*/
23+
protected function expectDeprecation($message)
24+
{
25+
if (!SymfonyTestsListenerTrait::$previousErrorHandler) {
26+
SymfonyTestsListenerTrait::$previousErrorHandler = set_error_handler([SymfonyTestsListenerTrait::class, 'handleError']);
27+
}
28+
29+
SymfonyTestsListenerTrait::$expectedDeprecations[] = $message;
30+
}
31+
}

src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313

1414
use Doctrine\Common\Annotations\AnnotationRegistry;
1515
use PHPUnit\Framework\AssertionFailedError;
16+
use PHPUnit\Framework\RiskyTestError;
1617
use PHPUnit\Framework\TestCase;
1718
use PHPUnit\Framework\TestSuite;
1819
use PHPUnit\Runner\BaseTestRunner;
1920
use PHPUnit\Util\Blacklist;
2021
use PHPUnit\Util\Test;
2122
use Symfony\Bridge\PhpUnit\ClockMock;
2223
use Symfony\Bridge\PhpUnit\DnsMock;
24+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
2325
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
2426
use Symfony\Component\ErrorHandler\DebugClassLoader;
2527

@@ -32,16 +34,16 @@
3234
*/
3335
class SymfonyTestsListenerTrait
3436
{
37+
public static $expectedDeprecations = [];
38+
public static $previousErrorHandler;
39+
private static $gatheredDeprecations = [];
3540
private static $globallyEnabled = false;
3641
private $state = -1;
3742
private $skippedFile = false;
3843
private $wasSkipped = [];
3944
private $isSkipped = [];
40-
private $expectedDeprecations = [];
41-
private $gatheredDeprecations = [];
42-
private $previousErrorHandler;
43-
private $error;
4445
private $runsInSeparateProcess = false;
46+
private $checkNumAssertions = false;
4547

4648
/**
4749
* @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive)
@@ -220,15 +222,17 @@ public function startTest($test)
220222
if (isset($annotations['class']['expectedDeprecation'])) {
221223
$test->getTestResultObject()->addError($test, new AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0);
222224
}
223-
if (isset($annotations['method']['expectedDeprecation'])) {
224-
if (!\in_array('legacy', $groups, true)) {
225-
$this->error = new AssertionFailedError('Only tests with the `@group legacy` annotation can have `@expectedDeprecation`.');
225+
if (isset($annotations['method']['expectedDeprecation']) || $this->checkNumAssertions = \in_array(ExpectDeprecationTrait::class, class_uses($test), true)) {
226+
if (isset($annotations['method']['expectedDeprecation'])) {
227+
self::$expectedDeprecations = $annotations['method']['expectedDeprecation'];
228+
self::$previousErrorHandler = set_error_handler([self::class, 'handleError']);
226229
}
227230

228-
$test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
231+
if ($this->checkNumAssertions) {
232+
$this->checkNumAssertions = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything() && !$test->doesNotPerformAssertions();
233+
}
229234

230-
$this->expectedDeprecations = $annotations['method']['expectedDeprecation'];
231-
$this->previousErrorHandler = set_error_handler([$this, 'handleError']);
235+
$test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
232236
}
233237
}
234238
}
@@ -242,9 +246,12 @@ public function endTest($test, $time)
242246
$className = \get_class($test);
243247
$groups = Test::getGroups($className, $test->getName(false));
244248

245-
if ($errored = null !== $this->error) {
246-
$test->getTestResultObject()->addError($test, $this->error, 0);
247-
$this->error = null;
249+
if ($this->checkNumAssertions) {
250+
if (!self::$expectedDeprecations && !$test->getNumAssertions()) {
251+
$test->getTestResultObject()->addFailure($test, new RiskyTestError('This test did not perform any assertions'), $time);
252+
}
253+
254+
$this->checkNumAssertions = false;
248255
}
249256

250257
if ($this->runsInSeparateProcess) {
@@ -263,24 +270,26 @@ public function endTest($test, $time)
263270
$this->runsInSeparateProcess = false;
264271
}
265272

266-
if ($this->expectedDeprecations) {
273+
if (self::$expectedDeprecations) {
267274
if (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE], true)) {
268-
$test->addToAssertionCount(\count($this->expectedDeprecations));
275+
$test->addToAssertionCount(\count(self::$expectedDeprecations));
269276
}
270277

271278
restore_error_handler();
272279

273-
if (!$errored && !\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
280+
if (!\in_array('legacy', $groups, true)) {
281+
$test->getTestResultObject()->addError($test, new AssertionFailedError('Only tests with the `@group legacy` annotation can expect a deprecation.'), 0);
282+
} elseif (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
274283
try {
275284
$prefix = "@expectedDeprecation:\n";
276-
$test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", $this->expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", $this->gatheredDeprecations)."\n");
285+
$test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", self::$expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", self::$gatheredDeprecations)."\n");
277286
} catch (AssertionFailedError $e) {
278287
$test->getTestResultObject()->addFailure($test, $e, $time);
279288
}
280289
}
281290

282-
$this->expectedDeprecations = $this->gatheredDeprecations = [];
283-
$this->previousErrorHandler = null;
291+
self::$expectedDeprecations = self::$gatheredDeprecations = [];
292+
self::$previousErrorHandler = null;
284293
}
285294
if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
286295
if (\in_array('time-sensitive', $groups, true)) {
@@ -292,10 +301,10 @@ public function endTest($test, $time)
292301
}
293302
}
294303

295-
public function handleError($type, $msg, $file, $line, $context = [])
304+
public static function handleError($type, $msg, $file, $line, $context = [])
296305
{
297306
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
298-
$h = $this->previousErrorHandler;
307+
$h = self::$previousErrorHandler;
299308

300309
return $h ? $h($type, $msg, $file, $line, $context) : false;
301310
}
@@ -308,7 +317,7 @@ public function handleError($type, $msg, $file, $line, $context = [])
308317
if (error_reporting()) {
309318
$msg = 'Unsilenced deprecation: '.$msg;
310319
}
311-
$this->gatheredDeprecations[] = $msg;
320+
self::$gatheredDeprecations[] = $msg;
312321

313322
return null;
314323
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\PhpUnit\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
16+
17+
final class ExpectDeprecationTraitTest extends TestCase
18+
{
19+
use ExpectDeprecationTrait;
20+
21+
/**
22+
* Do not remove this test in the next major version.
23+
*
24+
* @group legacy
25+
*/
26+
public function testOne()
27+
{
28+
$this->expectDeprecation('foo');
29+
@trigger_error('foo', E_USER_DEPRECATED);
30+
}
31+
32+
/**
33+
* Do not remove this test in the next major version.
34+
*
35+
* @group legacy
36+
*/
37+
public function testMany()
38+
{
39+
$this->expectDeprecation('foo');
40+
$this->expectDeprecation('bar');
41+
@trigger_error('foo', E_USER_DEPRECATED);
42+
@trigger_error('bar', E_USER_DEPRECATED);
43+
}
44+
45+
/**
46+
* Do not remove this test in the next major version.
47+
*
48+
* @group legacy
49+
*
50+
* @expectedDeprecation foo
51+
*/
52+
public function testOneWithAnnotation()
53+
{
54+
$this->expectDeprecation('bar');
55+
@trigger_error('foo', E_USER_DEPRECATED);
56+
@trigger_error('bar', E_USER_DEPRECATED);
57+
}
58+
59+
/**
60+
* Do not remove this test in the next major version.
61+
*
62+
* @group legacy
63+
*
64+
* @expectedDeprecation foo
65+
* @expectedDeprecation bar
66+
*/
67+
public function testManyWithAnnotation()
68+
{
69+
$this->expectDeprecation('ccc');
70+
$this->expectDeprecation('fcy');
71+
@trigger_error('foo', E_USER_DEPRECATED);
72+
@trigger_error('bar', E_USER_DEPRECATED);
73+
@trigger_error('ccc', E_USER_DEPRECATED);
74+
@trigger_error('fcy', E_USER_DEPRECATED);
75+
}
76+
}

0 commit comments

Comments
 (0)