Skip to content

Commit 950ed93

Browse files
author
Maxime Rainville
committed
Implement error logging and better testing set up
1 parent 38c064f commit 950ed93

File tree

5 files changed

+132
-8
lines changed

5 files changed

+132
-8
lines changed

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ Here's an example test:
251251
use Revolt\EventLoop;
252252
use SilverStripe\Dev\SapphireTest;
253253
use SilverStripe\Core\Injector\Injector;
254-
use ArchiPro\Silverstripe\EventDispatcher\Service\EventService;
254+
use ArchiPro\Silverstripe\EventDispatcher\Service\TestEventService;
255255

256256
class MyEventTest extends SapphireTest
257257
{
@@ -260,8 +260,9 @@ class MyEventTest extends SapphireTest
260260
// Create your test event
261261
$event = new MyCustomEvent('test message');
262262

263-
// Get the event service
264-
$service = Injector::inst()->get(EventService::class);
263+
// Get the Test Event Service ... this will replace the default EventService with a TestEventService
264+
// with an implementation that will log errors to help with debugging.
265+
$service = TestEventService::bootstrap();
265266

266267
// Add your test listener ... or if you have already
267268
$wasCalled = false;
@@ -281,6 +282,12 @@ class MyEventTest extends SapphireTest
281282
MyCustomEventListener::wasCalled(),
282283
'Assert some side effect of the event being handled'
283284
);
285+
286+
$this->assertCount(
287+
0,
288+
$service->getLogger()->records,
289+
'No errors were logged'
290+
);
284291
}
285292
}
286293
```

_config/events.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@ SilverStripe\Core\Injector\Injector:
77
# Define the listener provider
88
ArchiPro\EventDispatcher\ListenerProvider:
99
class: ArchiPro\EventDispatcher\ListenerProvider
10-
10+
1111
# Default event dispatcher
1212
ArchiPro\EventDispatcher\AsyncEventDispatcher:
1313
class: ArchiPro\EventDispatcher\AsyncEventDispatcher
1414
constructor:
1515
listenerProvider: '%$ArchiPro\EventDispatcher\ListenerProvider'
16+
logger: '%$Psr\Log\LoggerInterface.errorhandler'
1617
Psr\EventDispatcher\EventDispatcherInterface:
1718
alias: '%$ArchiPro\EventDispatcher\AsyncEventDispatcher'
1819

1920
# Bootstrap the event service
2021
ArchiPro\Silverstripe\EventDispatcher\Service\EventService:
21-
constructor:
22+
constructor:
2223
dispatcher: '%$ArchiPro\EventDispatcher\AsyncEventDispatcher'
23-
listenerProvider: '%$ArchiPro\EventDispatcher\ListenerProvider'
24+
listenerProvider: '%$ArchiPro\EventDispatcher\ListenerProvider'

composer.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
"silverstripe/versioned": "^1.13 || ^2.0",
1010
"psr/event-dispatcher": "^1.0",
1111
"psr/event-dispatcher-implementation": "^1.0",
12-
"archipro/revolt-event-dispatcher": "^0.0.0"
12+
"archipro/revolt-event-dispatcher": "^0.1.0"
1313
},
1414
"require-dev": {
1515
"phpunit/phpunit": "^9.5",
1616
"squizlabs/php_codesniffer": "^3.0",
1717
"friendsofphp/php-cs-fixer": "^3.0",
18-
"phpstan/phpstan": "^1.10"
18+
"phpstan/phpstan": "^1.10",
19+
"colinodell/psr-testlogger": "^1.3"
1920
},
2021
"autoload": {
2122
"psr-4": {

src/Service/TestEventService.php

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace ArchiPro\Silverstripe\EventDispatcher\Service;
4+
5+
use ArchiPro\EventDispatcher\AsyncEventDispatcher;
6+
use ArchiPro\EventDispatcher\ListenerProvider;
7+
use ColinODell\PsrTestLogger\TestLogger;
8+
use SilverStripe\Core\Injector\Injector;
9+
10+
/**
11+
* Extension of the AsyncEventDispatcher for testing purposes.
12+
*
13+
* This service will throw exceptions when errors occur to make it easier to debug issues.
14+
*/
15+
class TestEventService extends EventService
16+
{
17+
private ?TestLogger $logger = null;
18+
19+
public function __construct()
20+
{
21+
$listenerProvider = Injector::inst()->get(ListenerProvider::class);
22+
23+
// The test logger is useful but we don't want to force people to install it in production.
24+
if (class_exists(TestLogger::class)) {
25+
$this->logger = new TestLogger();
26+
}
27+
$dispatcher = new AsyncEventDispatcher($listenerProvider, $this->logger, AsyncEventDispatcher::THROW_ON_ERROR);
28+
parent::__construct($dispatcher, $listenerProvider);
29+
}
30+
31+
/**
32+
* Bootstrap the TestEventService. Will replace the default EventService with a TestEventService.
33+
*/
34+
public static function bootstrap(): self
35+
{
36+
$service = new self();
37+
Injector::inst()->registerService($service, AsyncEventDispatcher::class);
38+
return $service;
39+
}
40+
41+
/**
42+
* Return a logger that can be used to see if an array errors were thrown by the event loop.
43+
*/
44+
public function getLogger(): TestLogger
45+
{
46+
if (!$this->logger) {
47+
throw new \RuntimeException(
48+
'To use the EventService\'s test logger, you must install colinodell/psr-testlogger. ' .
49+
'`composer require --dev colinodell/psr-testlogger`'
50+
);
51+
}
52+
return $this->logger;
53+
}
54+
}
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace ArchiPro\Silverstripe\EventDispatcher\Tests\Service;
4+
5+
use ArchiPro\Silverstripe\EventDispatcher\Service\TestEventService;
6+
use Exception;
7+
use Revolt\EventLoop;
8+
use Revolt\EventLoop\UncaughtThrowable;
9+
use SilverStripe\Dev\SapphireTest;
10+
11+
class TestEventServiceTest extends SapphireTest
12+
{
13+
private TestEventService $service;
14+
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
$this->service = TestEventService::bootstrap();
19+
}
20+
21+
public function testEventDispatchLogger(): void
22+
{
23+
// Create test event
24+
$event = new class () {};
25+
26+
// Add test listener
27+
$this->service->addListener(get_class($event), function ($event) {
28+
throw new Exception('Test exception');
29+
});
30+
31+
// Dispatch event
32+
$result = $this->service->dispatch($event);
33+
34+
EventLoop::run();
35+
36+
$this->assertCount(
37+
1,
38+
$this->service->getLogger()->records,
39+
'Running the event loop will cause an error to be logged'
40+
);
41+
}
42+
43+
public function testEventDispatchThrow(): void
44+
{
45+
// Create test event
46+
$event = new class () {};
47+
48+
// Add test listener
49+
$this->service->addListener(get_class($event), function ($event) {
50+
throw new Exception('Test exception');
51+
});
52+
53+
$this->expectException(
54+
UncaughtThrowable::class,
55+
'Dispatching an event with a listener that throws an exception will throw an UncaughtThrowable'
56+
);
57+
58+
// Dispatch event
59+
$result = $this->service->dispatch($event)->await();
60+
}
61+
}

0 commit comments

Comments
 (0)