Logo

dev-resources.site

for different kinds of informations.

Using PHP anonymous classes to test collection services

Published at
3/15/2023
Categories
php
testing
phpunit
symfony
Author
icolomina
Categories
4 categories in total
php
open
testing
open
phpunit
open
symfony
open
Author
9 person written this
icolomina
open
Using PHP anonymous classes to test collection services

Many times we hold classes which follows the same contract into collections. An example would be a collection which holds services which send a notification to the user using different channels.

In this article we will see how to use anonymous classes to test we get the right service from a collection.

First of all, let's define the interface which our holded services will have to implement

interface NotificationHandlerInterface {

    public function send(mixed $data): void;
    public function getName(): string;
}
Enter fullscreen mode Exit fullscreen mode

Now, let's see how the service which looks into our collection looks like:

  class NotificationHandlerDiscover {

    /**
     * @var NotificationHandlerInterface[]
     */
    private array $collection;

    public function __construct(iterable $collection) {
        $this->collection = $collection;
    }

    public function getHandler(string $name): NotificationHandlerInterface {
        $handlers = array_filter(
            $this->collection, 
            fn(NotificationHandlerInterface $handler) => $handler->getName() === $name
        );
        if(count($handlers) === 0){
            throw new \InvalidArgumentException('There is no service named ' . $name);
        }

        return array_pop($handlers);
    }
}
Enter fullscreen mode Exit fullscreen mode

We don't mind how collections are loaded in NotificationHandlerDiscover since we only want to test it gets the right service acording to passed $name.
Let's see now how the test looks like

class NotificationHandlerDiscoverTest extends TestCase
{
    public function testEmailHandler()
    {
        $notificationHandlerDiscover = new NotificationHandlerDiscover($this->getCollection());
        $emailHandler = $notificationHandlerDiscover->getHandler('email');

        $this->assertInstanceOf(NotificationHandlerInterface::class, $emailHandler);
        $this->assertEquals('email', $emailHandler->getName() );
    }

    public function testSmsHandler()
    {
        $notificationHandlerDiscover = new NotificationHandlerDiscover($this->getCollection());
        $emailHandler = $notificationHandlerDiscover->getHandler('sms');

        $this->assertInstanceOf(NotificationHandlerInterface::class, $emailHandler);
        $this->assertEquals('sms', $emailHandler->getName() );
    }

    public function testUnexistingHandler()
    {
        $this->expectException(\InvalidArgumentException::class);
        $notificationHandlerDiscover = new NotificationHandlerDiscover($this->getCollection());
        $notificationHandlerDiscover->getHandler('telegram');
    }

    /**
     * @return NotificationHandlerInterface[]
     */
    private function getCollection(): array
    {
        $handlerEmail = new class implements NotificationHandlerInterface {

            public function send(mixed $data): void {  }

            public function getName(): string
            {
                return 'email';
            }
        };

        $handlerSms = new class implements NotificationHandlerInterface {

            public function send(mixed $data): void {  }

            public function getName(): string
            {
                return 'sms';
            }
        };

        return [
            $handlerSms,
            $handlerEmail
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

As we can see above, we also do not mind how handlers implentation works. We are testing if our discover handler gets the right service or throws the expected exception when such service does not exists.

Using anonymous classes we can create an "on the fly" array which holds two classes which implements NotificationHandlerInterface. We only need to write getName() method code which is used by the discover class to locate the service.

If you like my content and have learned with this article, you can take a look to my book: https://amzn.eu/d/3eO1DDi

phpunit Article's
30 articles in total
Favicon
6 Steps to Master PHPUnit Testing with Ease!
Favicon
Mastering Unit Testing in PHP: Tools, Frameworks, and Best Practices
Favicon
PHP: Should I mock or should I go?
Favicon
Focusing your tests on the domain. A PHPUnit example
Favicon
Understanding Mock Objects in PHPUnit Testing
Favicon
Wrote a book (And it is not about coding, and yeap it is in Greek)
Favicon
How to run a phpunit unit test with a specific dataset
Favicon
Test Your DOM in Laravel with PHPUnit
Favicon
PHP: Mocking Closures and performing assertions
Favicon
Run PHPUnit locally in your WordPress Plugin with DDEV
Favicon
Setting up for Drupal's Functional JavaScript tests
Favicon
Fix Memory Exhausted Issue in Laravel Tests
Favicon
Another way to create test module configuration
Favicon
Testando filas em projetos Laravel
Favicon
Checklist to Become Software Developer
Favicon
Chat : Test unitaire sur des méthodes privées
Favicon
Chat: Unit test of private methods
Favicon
Upgrading to Laravel 10, PHPUnit 10, and Pest 2
Favicon
Testing an external api using PHPUnit
Favicon
Installing PHP Unit
Favicon
Code coverage for PHPUnit in VSCode
Favicon
Behavior-Driven Testing with PHP and PHPUnit
Favicon
Run Laravel test multiple times
Favicon
Using PHP anonymous classes to test collection services
Favicon
Test coverage: did you set Xdebug's coverage mode?
Favicon
Using github actions to execute your PHP tests after every push
Favicon
Writing a basic Feature Test with PhpUnit in Laravel
Favicon
Fix Symfony tests with PHPUnit 10
Favicon
The most efficient way to debug problems with PHPUnit mocks
Favicon
Why I am unable to assert that method has been called on my mocked service?

Featured ones: