Skip to content

Commit

Permalink
feat(Testing): Add support for multiple listeners in assertEventListe…
Browse files Browse the repository at this point in the history
…ners

BREAKING CHANGE: assertEventListeners accepts list of contracts and their assert listener
  • Loading branch information
pionl committed Jan 16, 2023
1 parent 8c0ba4a commit 1428244
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 12 deletions.
34 changes: 30 additions & 4 deletions src/Testing/Concerns/AssertEventListeners.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,42 @@

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Events\Dispatcher;
use LaraStrict\Testing\AbstractExpectationCallMap;
use LaraStrict\Testing\AbstractExpectationCallsMap;
use PHPUnit\Framework\Assert;

trait AssertEventListeners
{
/**
* @template T of AbstractExpectationCallsMap|AbstractExpectationCallMap
* @param array<class-string, T> $contractMap
* @param bool $disableWildcard You can receive un-wanted listener responses (like laravel-ray). By default, we will remove any wildcard event.
* @param array|null $expectedListenerResults By default, assert returns nothing, we will auto populate nulls based on
*/
public function assertEventListeners(
Application $app,
object $event,
string $contract,
object $assert,
array $expectedListenerResults = [null],
array $contractMap,
?array $expectedListenerResults = null,
bool $disableWildcard = true
): void {
$app->bind(abstract: $contract, concrete: static fn () => $assert);
$shouldBuildResults = $expectedListenerResults === null;

if ($shouldBuildResults) {
$expectedListenerResults = [];
}

$asserts = [];

foreach ($contractMap as $contract => $assert) {
if ($shouldBuildResults) {
$expectedListenerResults[] = null;
}

$asserts[] = $assert;

$app->bind(abstract: $contract, concrete: static fn () => $assert);
}

/** @var Dispatcher $events */
$events = $app->make(Dispatcher::class);
Expand All @@ -33,5 +53,11 @@ public function assertEventListeners(
$results = $events->dispatch($event);

Assert::assertEquals($expectedListenerResults, $results);

foreach ($asserts as $assert) {
if ($assert instanceof AbstractExpectationCallsMap) {
$assert->assertCalled();
}
}
}
}
24 changes: 16 additions & 8 deletions tests/Feature/Testing/Concerns/AssertEventListenersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,28 @@ class AssertEventListenersTest extends TestCase
public function testAssertEventListeners(): void
{
// Set the event as provided would
/** @var Dispatcher $events */
$events = $this->app()
->get(Dispatcher::class);
$this->app()
->bind(TestListenerContract::class, TestListener::class);
$app = $this->app();

$app->bind(TestListenerContract::class, TestListener::class);
$app->bind(TestListenerContract::class, TestListener::class);

/** @var Dispatcher $events */
$events = $app->get(Dispatcher::class);
$events->listen(TestEvent::class, TestListenerContract::class);
$events->listen(TestEvent::class, TestListenerCallsContract::class);

$event = new TestEvent();
$this->assertEventListeners(
app: $this->app(),
app: $app,
event: $event,
contract: TestListenerContract::class,
assert: new TestListenerContractAssert([new TestListenerContractExpectation(event: $event)])
contractMap: [
TestListenerContract::class => new TestListenerContractAssert([
new TestListenerContractExpectation(event: $event),
]),
TestListenerCallsContract::class => new TestListenerCallsContractAssert([
new TestListenerCallsContractHandleExpectation(event: $event),
]),
],
);
}
}
20 changes: 20 additions & 0 deletions tests/Feature/Testing/Concerns/TestCallsListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Feature\Testing\Concerns;

use PHPUnit\Framework\Assert;

class TestCallsListener implements TestListenerCallsContract
{
public function handle(TestEvent $event): void
{
Assert::fail('Listener should not be called');
}

public function test(): void
{
Assert::fail('Test should not be called');
}
}
12 changes: 12 additions & 0 deletions tests/Feature/Testing/Concerns/TestListenerCallsContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Feature\Testing\Concerns;

interface TestListenerCallsContract
{
public function handle(TestEvent $event): void;

public function test(): void;
}
42 changes: 42 additions & 0 deletions tests/Feature/Testing/Concerns/TestListenerCallsContractAssert.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Feature\Testing\Concerns;

use LaraStrict\Testing\AbstractExpectationCallsMap;
use PHPUnit\Framework\Assert;

class TestListenerCallsContractAssert extends AbstractExpectationCallsMap implements TestListenerCallsContract
{
/**
* @param array<TestListenerCallsContractHandleExpectation> $handle
* @param array<TestListenerCallsContractTestExpectation> $test
*/
public function __construct(array $handle = [], array $test = [])
{
$this->setExpectations(TestListenerCallsContractHandleExpectation::class, array_values(array_filter($handle)));
$this->setExpectations(TestListenerCallsContractTestExpectation::class, array_values(array_filter($test)));
}

public function handle(TestEvent $event): void
{
$expectation = $this->getExpectation(TestListenerCallsContractHandleExpectation::class);
$message = $this->getDebugMessage();

Assert::assertEquals($expectation->event, $event, $message);

if (is_callable($expectation->hook)) {
call_user_func($expectation->hook, $event, $expectation);
}
}

public function test(): void
{
$expectation = $this->getExpectation(TestListenerCallsContractTestExpectation::class);

if (is_callable($expectation->hook)) {
call_user_func($expectation->hook, $expectation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Feature\Testing\Concerns;

use Closure;

final class TestListenerCallsContractHandleExpectation
{
/**
* @param Closure(TestEvent,self):void|null $hook
*/
public function __construct(
public readonly TestEvent $event,
public readonly ?Closure $hook = null,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Feature\Testing\Concerns;

use Closure;

final class TestListenerCallsContractTestExpectation
{
/**
* @param Closure(self):void|null $hook
*/
public function __construct(
public readonly ?Closure $hook = null
) {
}
}

0 comments on commit 1428244

Please sign in to comment.