Skip to content

Commit 29fad8c

Browse files
committed
feature #36003 [ErrorHandler][FrameworkBundle] better error messages in failing tests (guillbdx)
This PR was squashed before being merged into the 5.1-dev branch. Discussion ---------- [ErrorHandler][FrameworkBundle] better error messages in failing tests | Q | A | ------------- | --- | Branch? | master for features | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #32752 | License | MIT | Doc PR | Purpose of this PR is to enhance tests by giving a way to report an exception that occured during the processing of the request. The ErrorHandler will add an X-Debug-Exception, and the assertThat() method of WebTestCase will throw an exception if this header exists and status code is 5xx. In practice, this adds the "Caused by" section in this example: ``` Time: 374 ms, Memory: 20.00 MB There was 1 failure: 1) App\Tests\Controller\HomeControllerTest::testC Failed asserting that the Response has header "Content-Type" with value "application/json". /srv/symfony/src/Symfony/Bundle/FrameworkBundle/Test/BrowserKitAssertionsTrait.php:132 /srv/symfony/src/Symfony/Bundle/FrameworkBundle/Test/BrowserKitAssertionsTrait.php:66 /srv/blog/tests/Controller/HomeControllerTest.php:29 Caused by Exception: This a test exception. in /the/file.php:139 Stack trace: [...] ``` Commits ------- 0da9469ee2 [ErrorHandler][FrameworkBundle] better error messages in failing tests
2 parents a386b6d + 542cdc6 commit 29fad8c

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ CHANGELOG
1414
* Deprecated *not* setting the "framework.router.utf8" configuration option as it will default to `true` in Symfony 6.0
1515
* Added tag `routing.expression_language_function` to define functions available in route conditions
1616
* Added `debug:container --deprecations` option to see compile-time deprecations.
17+
* Made `BrowserKitAssertionsTrait` report the original error message in case of a failure
1718

1819
5.0.0
1920
-----

Test/BrowserKitAssertionsTrait.php

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Test;
1313

14+
use PHPUnit\Framework\Constraint\Constraint;
1415
use PHPUnit\Framework\Constraint\LogicalAnd;
1516
use PHPUnit\Framework\Constraint\LogicalNot;
17+
use PHPUnit\Framework\ExpectationFailedException;
1618
use Symfony\Component\BrowserKit\AbstractBrowser;
1719
use Symfony\Component\BrowserKit\Test\Constraint as BrowserKitConstraint;
1820
use Symfony\Component\HttpFoundation\Request;
@@ -28,12 +30,12 @@ trait BrowserKitAssertionsTrait
2830
{
2931
public static function assertResponseIsSuccessful(string $message = ''): void
3032
{
31-
self::assertThat(self::getResponse(), new ResponseConstraint\ResponseIsSuccessful(), $message);
33+
self::assertThatForResponse(new ResponseConstraint\ResponseIsSuccessful(), $message);
3234
}
3335

3436
public static function assertResponseStatusCodeSame(int $expectedCode, string $message = ''): void
3537
{
36-
self::assertThat(self::getResponse(), new ResponseConstraint\ResponseStatusCodeSame($expectedCode), $message);
38+
self::assertThatForResponse(new ResponseConstraint\ResponseStatusCodeSame($expectedCode), $message);
3739
}
3840

3941
public static function assertResponseRedirects(string $expectedLocation = null, int $expectedCode = null, string $message = ''): void
@@ -46,42 +48,42 @@ public static function assertResponseRedirects(string $expectedLocation = null,
4648
$constraint = LogicalAnd::fromConstraints($constraint, new ResponseConstraint\ResponseStatusCodeSame($expectedCode));
4749
}
4850

49-
self::assertThat(self::getResponse(), $constraint, $message);
51+
self::assertThatForResponse($constraint, $message);
5052
}
5153

5254
public static function assertResponseHasHeader(string $headerName, string $message = ''): void
5355
{
54-
self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHasHeader($headerName), $message);
56+
self::assertThatForResponse(new ResponseConstraint\ResponseHasHeader($headerName), $message);
5557
}
5658

5759
public static function assertResponseNotHasHeader(string $headerName, string $message = ''): void
5860
{
59-
self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHasHeader($headerName)), $message);
61+
self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHasHeader($headerName)), $message);
6062
}
6163

6264
public static function assertResponseHeaderSame(string $headerName, string $expectedValue, string $message = ''): void
6365
{
64-
self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue), $message);
66+
self::assertThatForResponse(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue), $message);
6567
}
6668

6769
public static function assertResponseHeaderNotSame(string $headerName, string $expectedValue, string $message = ''): void
6870
{
69-
self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue)), $message);
71+
self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue)), $message);
7072
}
7173

7274
public static function assertResponseHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
7375
{
74-
self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHasCookie($name, $path, $domain), $message);
76+
self::assertThatForResponse(new ResponseConstraint\ResponseHasCookie($name, $path, $domain), $message);
7577
}
7678

7779
public static function assertResponseNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void
7880
{
79-
self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHasCookie($name, $path, $domain)), $message);
81+
self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHasCookie($name, $path, $domain)), $message);
8082
}
8183

8284
public static function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', string $domain = null, string $message = ''): void
8385
{
84-
self::assertThat(self::getResponse(), LogicalAnd::fromConstraints(
86+
self::assertThatForResponse(LogicalAnd::fromConstraints(
8587
new ResponseConstraint\ResponseHasCookie($name, $path, $domain),
8688
new ResponseConstraint\ResponseCookieValueSame($name, $expectedValue, $path, $domain)
8789
), $message);
@@ -124,6 +126,21 @@ public static function assertRouteSame($expectedRoute, array $parameters = [], s
124126
self::assertThat(self::getRequest(), $constraint, $message);
125127
}
126128

129+
public static function assertThatForResponse(Constraint $constraint, string $message = ''): void
130+
{
131+
try {
132+
self::assertThat(self::getResponse(), $constraint, $message);
133+
} catch (ExpectationFailedException $exception) {
134+
if (($serverExceptionMessage = self::getResponse()->headers->get('X-Debug-Exception'))
135+
&& ($serverExceptionFile = self::getResponse()->headers->get('X-Debug-Exception-File'))) {
136+
$serverExceptionFile = explode(':', $serverExceptionFile);
137+
$exception->__construct($exception->getMessage(), $exception->getComparisonFailure(), new \ErrorException(rawurldecode($serverExceptionMessage), 0, 1, rawurldecode($serverExceptionFile[0]), $serverExceptionFile[1]), $exception->getPrevious());
138+
}
139+
140+
throw $exception;
141+
}
142+
}
143+
127144
private static function getClient(AbstractBrowser $newClient = null): ?AbstractBrowser
128145
{
129146
static $client;

Tests/Test/WebTestCaseTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Test;
1313

1414
use PHPUnit\Framework\AssertionFailedError;
15+
use PHPUnit\Framework\ExpectationFailedException;
1516
use PHPUnit\Framework\TestCase;
1617
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
1718
use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait;
@@ -235,6 +236,17 @@ public function testAssertRouteSame()
235236
$this->getRequestTester()->assertRouteSame('articles');
236237
}
237238

239+
public function testExceptionOnServerError()
240+
{
241+
try {
242+
$this->getResponseTester(new Response('', 500, ['X-Debug-Exception' => 'An exception has occurred', 'X-Debug-Exception-File' => '%2Fsrv%2Ftest.php:12']))->assertResponseIsSuccessful();
243+
} catch (ExpectationFailedException $exception) {
244+
$this->assertSame('An exception has occurred', $exception->getPrevious()->getMessage());
245+
$this->assertSame('/srv/test.php', $exception->getPrevious()->getFile());
246+
$this->assertSame(12, $exception->getPrevious()->getLine());
247+
}
248+
}
249+
238250
private function getResponseTester(Response $response): WebTestCase
239251
{
240252
$client = $this->createMock(KernelBrowser::class);

0 commit comments

Comments
 (0)