Skip to content
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

TesterBundle new EventDispatcherTesterTrait #287

Merged
merged 2 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"doctrine/persistence": "^2.2 || ^3.0",
"dragonmantank/cron-expression": "^3.3",
"ext-ctype": "*",
"ext-dom": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-mongodb": "*",
Expand Down Expand Up @@ -47,6 +48,7 @@
"symfony/console": "^6.4.0",
"symfony/css-selector": "^6.4.0",
"symfony/dependency-injection": "^6.4.0",
"symfony/deprecation-contracts": "^3.5.0",
"symfony/doctrine-bridge": "^6.4.0",
"symfony/doctrine-messenger": "^6.4.0",
"symfony/dom-crawler": "^6.4.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Draw\Bundle\TesterBundle\EventDispatcher;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

trait EventDispatcherTesterTrait
{
protected static array $eventDispatcherFileCleaners = [
[EventDispatcherTesterTrait::class, 'cleanEventDispatcherFileContainerReference'],
];

protected static function cleanEventDispatcherFile(string $filePath): void
{
foreach (static::$eventDispatcherFileCleaners as $cleaner) {
\call_user_func($cleaner, $filePath);
}
}

/**
* Clean reference in file that looks like this.
*
* <callable type="function" name="onKernelControllerArguments" class="ContainerPKL8sSi\RequestPayloadValueResolverGhostB42fd45" priority="0"/>
*
* By Removing the unique string pattern in the class attribute using DomDocument.
*
* The result after cleaning will be:
*
* <callable type="function" name="onKernelControllerArguments" class="Container\RequestPayloadValueResolverGhost" priority="0"/>
*
* Clean when the class name contains Container and Ghost in its name.
*/
public static function cleanEventDispatcherFileContainerReference(string $filePath): void
{
$dom = new \DOMDocument();
$dom->load($filePath);

$xpath = new \DOMXPath($dom);

$nodes = $xpath->query('//callable[@class]');

foreach ($nodes as $node) {
\assert($node instanceof \DOMElement);
$class = $node->getAttribute('class');
if (preg_match('/Container.*Ghost/', $class)) {
// Use regex to match only the alphanumeric patterns that follow "Container" and "Ghost"
$class = preg_replace('/(?<=Container)[A-Za-z0-9]+|(?<=Ghost)[A-Za-z0-9]+/', '__cleaned__', $class);
$node->setAttribute('class', $class);
}
}

$dom->save($filePath);
}

public static function assertEventDispatcherConfiguration(string $expectedFilePath, string $dispatcherName = 'event_dispatcher'): void
{
$commandTester = new CommandTester(
static::getContainer()->get('console.command.event_dispatcher_debug')
);

$commandTester->execute([
'--dispatcher' => $dispatcherName,
'--format' => 'xml',
]);

$display = $commandTester->getDisplay();

$resultFilePath = tempnam(sys_get_temp_dir(), 'event_dispatcher_configuration');

file_put_contents($resultFilePath, $display);

static::cleanEventDispatcherFile($resultFilePath);

register_shutdown_function('unlink', $resultFilePath);

if (!file_exists($expectedFilePath)) {
copy($resultFilePath, $expectedFilePath);

TestCase::fail($dispatcherName.' configuration file created at '.$expectedFilePath.'. Please review it and commit it.');
}

TestCase::assertXmlFileEqualsXmlFile(
$expectedFilePath,
$resultFilePath,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

use PHPUnit\Framework\TestCase;

trigger_deprecation('draw/tester-bundle', '0.10.44', 'The "%s" class is deprecated, use "%s" instead.', EventListenerTestTrait::class, EventDispatcherTesterTrait::class);

/**
* @deprecated
*/
trait EventListenerTestTrait
{
public static function assertEventListenersRegistered(string $className, array $expectedEvents): void
Expand Down
47 changes: 32 additions & 15 deletions packages/tester-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,46 @@ Draw Tester Bundle

This bundle integrate the Draw Tester Component.

To use the HttpTesterTrait in a KernelTestCase you must simply do this:
It also provides test helpers to make it easier to test your Symfony application.

```PHP
<?php namespace App\Tests;
## Kernel Testing

use Draw\Bundle\TesterBundle\Http\BrowserFactoryInterface;
use Draw\Bundle\TesterBundle\Http\HttpTesterTrait;
When configuring your kernel you may want to test that everything is hooked up correctly.

There is the list of service, event dispatcher, command etc.

There is some TestCase/Trait to help you do that (work in progress).

### Event Dispatcher

Relying on the `debug:event-dispatcher` command we can dump the list of event listeners and validated it against the expected list.

```php
<?php

namespace App\Tests;

use Draw\Bundle\TesterBundle\EventDispatcher\EventDispatcherTesterTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\BrowserKit\AbstractBrowser;

class TestCase extends KernelTestCase implements BrowserFactoryInterface
class AppKernelTest extends KernelTestCase
{
use HttpTesterTrait;
public function createBrowser(): AbstractBrowser
use EventDispatcherTesterTrait;

public function testEventDispatcherConfiguration(): void
{
return static::bootKernel()->getContainer()->get('test.client');
$this->assertEventDispatcherConfiguration(
__DIR__.'/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml',
'event_dispatcher' // This is the default value, same as the debug:event-dispatcher command
);
}
}
```

As you can see we are using the **HttpTesterTrait** of the **Bundle** instead of the **Component**.
This is because it as the implementation of the implementation of the **createHttpTesterClient** method.
The first time you run this test it will fail and dump the current configuration in the `event_dispatcher.xml` file.

Commit this file, next time your rune this test you will be able to validate that the configuration is still valid.

If you change the listener in your code or change your dependencies you can run the test again and see the diff.

Also you can see that we are booting a new kernel every time. It's to make sure we are using a new container
on each request like the behaviour of a normal client request will do.
This will allow you to see if some external listeners changed at the same time.
2 changes: 2 additions & 0 deletions packages/tester-bundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"integration test"
],
"require": {
"ext-dom": "*",
"draw/core": "^0.11",
"draw/tester": "^0.11",
"symfony/browser-kit": "^6.4.0",
"symfony/deprecation-contracts": "^3.5.0",
"symfony/framework-bundle": "^6.4.0",
"symfony/http-foundation": "^6.4.0",
"symfony/validator": "^6.4.0"
Expand Down
18 changes: 18 additions & 0 deletions tests/AppKernelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Tests;

use Draw\Bundle\TesterBundle\EventDispatcher\EventDispatcherTesterTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class AppKernelTest extends KernelTestCase
{
use EventDispatcherTesterTrait;

public function testEventDispatcherConfiguration(): void
{
$this->assertEventDispatcherConfiguration(
__DIR__.'/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml'
);
}
}
30 changes: 0 additions & 30 deletions tests/Mailer/EventListener/EmailComposerListenerTest.php

This file was deleted.

Loading
Loading