Skip to content

Commit c871359

Browse files
author
Maxime Rainville
committed
Implement error logging and better testing set up
1 parent 4c61c6e commit c871359

File tree

5 files changed

+77
-14
lines changed

5 files changed

+77
-14
lines changed

README.md

+12-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,10 +282,18 @@ 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
```
287294

295+
296+
288297
### Disabling event dispatching
289298

290299
You can disable event dispatching for test to avoid side affects from irrelevant events that might be fired while

_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": "dev-pull/0/add-better-error-handling"
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

+27-2
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,50 @@
44

55
use ArchiPro\EventDispatcher\AsyncEventDispatcher;
66
use ArchiPro\EventDispatcher\ListenerProvider;
7+
use ColinODell\PsrTestLogger\TestLogger;
78
use SilverStripe\Core\Injector\Injector;
89

910
/**
1011
* Extension of the AsyncEventDispatcher for testing purposes.
11-
*
12+
*
1213
* This service will throw exceptions when errors occur to make it easier to debug issues.
1314
*/
1415
class TestEventService extends EventService
1516
{
17+
private ?TestLogger $logger = null;
18+
1619
public function __construct() {
1720
$listenerProvider = Injector::inst()->get(ListenerProvider::class);
18-
$dispatcher = new AsyncEventDispatcher($listenerProvider, null, AsyncEventDispatcher::THROW_ON_ERROR);
21+
22+
// The test logger is useful but we don't want to force people to install it in production.
23+
if (class_exists(TestLogger::class)) {
24+
$this->logger = new TestLogger();
25+
}
26+
$dispatcher = new AsyncEventDispatcher($listenerProvider, $this->logger, AsyncEventDispatcher::THROW_ON_ERROR);
1927
parent::__construct($dispatcher, $listenerProvider);
2028
}
2129

30+
/**
31+
* Bootstrap the TestEventService. Will replace the default EventService with a TestEventService.
32+
*/
2233
public static function bootstrap(): self
2334
{
2435
$service = new self();
2536
Injector::inst()->registerService($service, AsyncEventDispatcher::class);
2637
return $service;
2738
}
39+
40+
/**
41+
* Return a logger that can be used to see if an array errors were thrown by the event loop.
42+
*/
43+
public function getLogger(): TestLogger
44+
{
45+
if (!$this->logger) {
46+
throw new \RuntimeException(
47+
'To use the EventService\'s test logger, you must install colinodell/psr-testlogger. ' .
48+
'`composer require --dev colinodell/psr-testlogger`'
49+
);
50+
}
51+
return $this->logger;
52+
}
2853
}

tests/php/Service/TestEventServiceTest.php

+31-4
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22

33
namespace ArchiPro\Silverstripe\EventDispatcher\Tests\Service;
44

5-
use ArchiPro\Silverstripe\EventDispatcher\Service\EventService;
65
use ArchiPro\Silverstripe\EventDispatcher\Service\TestEventService;
76
use ArchiPro\Silverstripe\EventDispatcher\Tests\TestListenerLoader;
87
use Exception;
98
use Revolt\EventLoop;
109
use SilverStripe\Core\Injector\Injector;
1110
use SilverStripe\Dev\SapphireTest;
11+
use Amp\Future\UnhandledFutureError;
12+
use Revolt\EventLoop\UncaughtThrowable;
1213

1314
class TestEventServiceTest extends SapphireTest
1415
{
15-
private EventService $service;
16-
16+
private TestEventService $service;
17+
1718
protected function setUp(): void
1819
{
20+
parent::setUp();
1921
$this->service = TestEventService::bootstrap();
2022
}
2123

22-
public function testEventDispatch(): void
24+
public function testEventDispatchLogger(): void
2325
{
2426
// Create test event
2527
$event = new class () {};
@@ -33,5 +35,30 @@ public function testEventDispatch(): void
3335
$result = $this->service->dispatch($event);
3436

3537
EventLoop::run();
38+
39+
$this->assertCount(
40+
1,
41+
$this->service->getLogger()->records,
42+
'Running the event loop will cause an error to be logged'
43+
);
44+
}
45+
46+
public function testEventDispatchThrow(): void
47+
{
48+
// Create test event
49+
$event = new class () {};
50+
51+
// Add test listener
52+
$this->service->addListener(get_class($event), function ($event) {
53+
throw new Exception('Test exception');
54+
});
55+
56+
$this->expectException(
57+
UncaughtThrowable::class,
58+
'Dispatching an event with a listener that throws an exception will throw an UncaughtThrowable'
59+
);
60+
61+
// Dispatch event
62+
$result = $this->service->dispatch($event)->await();
3663
}
3764
}

0 commit comments

Comments
 (0)