Issue #2795567 by joachim, jungle, daffie, Sophie.SK, mondrake, ravi.shankar, jonathanshaw, dawehner, AaronBauman, alexpott: Use Symfony's VarDumper for easier test debugging with dump()

merge-requests/4973/head
Alex Pott 2021-02-08 22:42:48 +00:00
parent c377d26143
commit f7dd26aa69
No known key found for this signature in database
GPG Key ID: 31905460D4A69276
10 changed files with 228 additions and 0 deletions

View File

@ -2,6 +2,8 @@
namespace Drupal\test_page_test\Controller; namespace Drupal\test_page_test\Controller;
use Drupal\user\Entity\Role;
/** /**
* Controller routines for test_page_test routes. * Controller routines for test_page_test routes.
*/ */
@ -23,4 +25,16 @@ class TestPageTestController {
]; ];
} }
/**
* Returns a test page and with the call to the dump() function.
*/
public function testPageVarDump() {
$role = Role::create(['id' => 'test_role']);
dump($role);
return [
'#title' => t('Test page with var dump'),
'#markup' => t('Test page text.'),
];
}
} }

View File

@ -144,3 +144,11 @@ test_page_test.deprecations:
_controller: '\Drupal\test_page_test\Controller\Test::deprecations' _controller: '\Drupal\test_page_test\Controller\Test::deprecations'
requirements: requirements:
_access: 'TRUE' _access: 'TRUE'
test_page_test.test_page_var_dump:
path: '/test-page-var-dump'
defaults:
_title: 'Test front page with var dump'
_controller: '\Drupal\test_page_test\Controller\TestPageTestController::testPageVarDump'
requirements:
_access: 'TRUE'

View File

@ -8,7 +8,9 @@ use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\StreamCapturer;
use Drupal\Tests\Traits\Core\CronRunTrait; use Drupal\Tests\Traits\Core\CronRunTrait;
use Drupal\user\Entity\Role;
use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\ExpectationFailedException;
/** /**
@ -949,4 +951,39 @@ class BrowserTestBaseTest extends BrowserTestBase {
$this->drupalGetHeader('Content-Type'); $this->drupalGetHeader('Content-Type');
} }
/**
* Tests the dump() function provided by the var-dumper Symfony component.
*/
public function testVarDump() {
// Append the stream capturer to the STDOUT stream, so that we can test the
// dump() output and also prevent it from actually outputting in this
// particular test.
stream_filter_register("capture", StreamCapturer::class);
stream_filter_append(STDOUT, "capture");
// Dump some variables to check that dump() in test code produces output
// on the command line that is running the test.
$role = Role::load('authenticated');
dump($role);
dump($role->id());
$this->assertStringContainsString('Drupal\user\Entity\Role', StreamCapturer::$cache);
$this->assertStringContainsString('authenticated', StreamCapturer::$cache);
// Visit a Drupal page with call to the dump() function to check that dump()
// in site code produces output in the requested web page's HTML.
$body = $this->drupalGet('test-page-var-dump');
$this->assertSession()->statusCodeEquals(200);
// It is too strict to assert all properties of the Role and it is easy to
// break if one of these properties gets removed or gets a new default
// value. It should be sufficient to test just a couple of properties.
$this->assertStringContainsString('<span class=sf-dump-note>', $body);
$this->assertStringContainsString(' #<span class=sf-dump-protected title="Protected property">id</span>: "<span class=sf-dump-str title="9 characters">test_role</span>"', $body);
$this->assertStringContainsString(' #<span class=sf-dump-protected title="Protected property">label</span>: <span class=sf-dump-const>null</span>', $body);
$this->assertStringContainsString(' #<span class=sf-dump-protected title="Protected property">permissions</span>: []', $body);
$this->assertStringContainsString(' #<span class=sf-dump-protected title="Protected property">uuid</span>: "', $body);
$this->assertStringContainsString('</samp>}', $body);
}
} }

View File

@ -22,6 +22,7 @@ use Drupal\Tests\PhpUnitCompatibilityTrait;
use Drupal\Tests\TestRequirementsTrait; use Drupal\Tests\TestRequirementsTrait;
use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\Tests\Traits\PhpUnitWarnings;
use Drupal\TestTools\Comparator\MarkupInterfaceComparator; use Drupal\TestTools\Comparator\MarkupInterfaceComparator;
use Drupal\TestTools\TestVarDumper;
use PHPUnit\Framework\Exception; use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
@ -30,6 +31,7 @@ use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\visitor\vfsStreamPrintVisitor; use org\bovigo\vfs\visitor\vfsStreamPrintVisitor;
use Drupal\Core\Routing\RouteObjectInterface; use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\VarDumper\VarDumper;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
/** /**
@ -64,6 +66,9 @@ use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
* KernelTestBase::installEntitySchema(). Alternately, tests which need modules * KernelTestBase::installEntitySchema(). Alternately, tests which need modules
* to be fully installed could inherit from \Drupal\Tests\BrowserTestBase. * to be fully installed could inherit from \Drupal\Tests\BrowserTestBase.
* *
* Using Symfony's dump() function in Kernel tests will produce output on the
* command line, whether the call to dump() is in test code or site code.
*
* @see \Drupal\Tests\KernelTestBase::$modules * @see \Drupal\Tests\KernelTestBase::$modules
* @see \Drupal\Tests\KernelTestBase::enableModules() * @see \Drupal\Tests\KernelTestBase::enableModules()
* @see \Drupal\Tests\KernelTestBase::installConfig() * @see \Drupal\Tests\KernelTestBase::installConfig()
@ -223,6 +228,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa
*/ */
public static function setUpBeforeClass() { public static function setUpBeforeClass() {
parent::setUpBeforeClass(); parent::setUpBeforeClass();
VarDumper::setHandler(TestVarDumper::class . '::cliHandler');
// Change the current dir to DRUPAL_ROOT. // Change the current dir to DRUPAL_ROOT.
chdir(static::getDrupalRoot()); chdir(static::getDrupalRoot());

View File

@ -5,6 +5,8 @@ namespace Drupal\KernelTests;
use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Component\FileCache\FileCacheFactory;
use Drupal\Core\Database\Database; use Drupal\Core\Database\Database;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use Drupal\Tests\StreamCapturer;
use Drupal\user\Entity\Role;
use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\visitor\vfsStreamStructureVisitor; use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;
use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\SkippedTestError;
@ -396,4 +398,24 @@ class KernelTestBaseTest extends KernelTestBase {
$this->assertFalse(Database::getConnection()->schema()->tableExists('key_value')); $this->assertFalse(Database::getConnection()->schema()->tableExists('key_value'));
} }
/**
* Tests the dump() function provided by the var-dumper Symfony component.
*/
public function testVarDump() {
// Append the stream capturer to the STDOUT stream, so that we can test the
// dump() output and also prevent it from actually outputting in this
// particular test.
stream_filter_register("capture", StreamCapturer::class);
stream_filter_append(STDOUT, "capture");
// Dump some variables.
$this->enableModules(['system', 'user']);
$role = Role::create(['id' => 'test_role']);
dump($role);
dump($role->id());
$this->assertStringContainsString('Drupal\user\Entity\Role', StreamCapturer::$cache);
$this->assertStringContainsString('test_role', StreamCapturer::$cache);
}
} }

View File

@ -0,0 +1,46 @@
<?php
namespace Drupal\TestTools;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
/**
* Provides handlers for the Symfony VarDumper to work within tests.
*
* This allows the dump() function to produce output on the terminal without
* causing PHPUnit to complain.
*/
class TestVarDumper {
/**
* A CLI handler for \Symfony\Component\VarDumper\VarDumper.
*/
public static function cliHandler($var) {
$cloner = new VarCloner();
$dumper = new CliDumper();
fwrite(STDOUT, "\n");
$dumper->setColors(TRUE);
$dumper->dump(
$cloner->cloneVar($var),
function ($line, $depth, $indent_pad) {
// A negative depth means "end of dump".
if ($depth >= 0) {
// Adds a two spaces indentation to the line.
fwrite(STDOUT, str_repeat($indent_pad, $depth) . $line . "\n");
}
}
);
}
/**
* A HTML handler for \Symfony\Component\VarDumper\VarDumper.
*/
public static function htmlHandler($var) {
$cloner = new VarCloner();
$dumper = new HtmlDumper();
$dumper->dump($cloner->cloneVar($var));
}
}

View File

@ -20,10 +20,12 @@ use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\Tests\Traits\PhpUnitWarnings;
use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\TestTools\Comparator\MarkupInterfaceComparator; use Drupal\TestTools\Comparator\MarkupInterfaceComparator;
use Drupal\TestTools\TestVarDumper;
use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Cookie\CookieJar;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\CssSelector\CssSelectorConverter; use Symfony\Component\CssSelector\CssSelectorConverter;
use Symfony\Component\VarDumper\VarDumper;
/** /**
* Provides a test case for functional Drupal tests. * Provides a test case for functional Drupal tests.
@ -36,6 +38,11 @@ use Symfony\Component\CssSelector\CssSelectorConverter;
* translation functionality. For example, avoid wrapping test text with t() * translation functionality. For example, avoid wrapping test text with t()
* or TranslatableMarkup(). * or TranslatableMarkup().
* *
* Using Symfony's dump() function in functional test test code will produce
* output on the command line; using dump() in site code will produce output in
* the requested web page, which can then be inspected in the HTML output from
* the test.
*
* @ingroup testing * @ingroup testing
*/ */
abstract class BrowserTestBase extends TestCase { abstract class BrowserTestBase extends TestCase {
@ -214,6 +221,14 @@ abstract class BrowserTestBase extends TestCase {
*/ */
protected $originalContainer; protected $originalContainer;
/**
* {@inheritdoc}
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
VarDumper::setHandler(TestVarDumper::class . '::cliHandler');
}
/** /**
* Initializes Mink sessions. * Initializes Mink sessions.
*/ */

View File

@ -0,0 +1,23 @@
<?php
namespace Drupal\Tests;
/**
* Captures output to a stream and stores it for retrieval.
*/
class StreamCapturer extends \php_user_filter {
public static $cache = '';
public function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
self::$cache .= $bucket->data;
// cSpell:disable-next-line
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
// cSpell:disable-next-line
return PSFS_FEED_ME;
}
}

View File

@ -10,12 +10,17 @@ use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\Tests\Traits\PhpUnitWarnings;
use Drupal\TestTools\TestVarDumper;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\VarDumper;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
/** /**
* Provides a base class and helpers for Drupal unit tests. * Provides a base class and helpers for Drupal unit tests.
* *
* Using Symfony's dump() function() in Unit tests will produce output on the
* command line.
*
* @ingroup testing * @ingroup testing
*/ */
abstract class UnitTestCase extends TestCase { abstract class UnitTestCase extends TestCase {
@ -38,6 +43,14 @@ abstract class UnitTestCase extends TestCase {
*/ */
protected $root; protected $root;
/**
* {@inheritdoc}
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
VarDumper::setHandler(TestVarDumper::class . '::cliHandler');
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -19,4 +19,48 @@ class UnitTestCaseTest extends UnitTestCase {
$this->assertArrayEquals([], []); $this->assertArrayEquals([], []);
} }
/**
* Tests the dump() function in a test run in the same process.
*/
public function testVarDumpSameProcess() {
// Append the stream capturer to the STDOUT stream, so that we can test the
// dump() output and also prevent it from actually outputting in this
// particular test.
stream_filter_register("capture", StreamCapturer::class);
stream_filter_append(STDOUT, "capture");
// Dump some variables.
$object = (object) [
'foo' => 'bar',
];
dump($object);
dump('banana');
$this->assertStringContainsString('bar', StreamCapturer::$cache);
$this->assertStringContainsString('banana', StreamCapturer::$cache);
}
/**
* Tests the dump() function in a test run in a separate process.
*
* @runInSeparateProcess
*/
public function testVarDumpSeparateProcess() {
// Append the stream capturer to the STDOUT stream, so that we can test the
// dump() output and also prevent it from actually outputting in this
// particular test.
stream_filter_register("capture", StreamCapturer::class);
stream_filter_append(STDOUT, "capture");
// Dump some variables.
$object = (object) [
'foo' => 'bar',
];
dump($object);
dump('banana');
$this->assertStringContainsString('bar', StreamCapturer::$cache);
$this->assertStringContainsString('banana', StreamCapturer::$cache);
}
} }