もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

cakephp3でeventを使っている際のテストコード

はじめに

こんばんは。 またまたcakephp3記事です。

もう cakephpver4.2.4とかなのですが、現場ではまだcakephp3を使用しているため致し方なし。という感じです。

今回は cakephpのevent機能を使用している際のテストコードです。

前回 eventの使用方法などは書いたのですが、使用している箇所のテストコードを今回書いたので、備忘録として残しておきます。

kojirooooocks.hatenablog.com

本題

前回使用した ExampleControllerのテストコードを書きます。

今回はわかりやすく UserContollerにして、actionも registerにします。

使用されているeventは NotificationEventListener です。

今回は少しだけ具体的に NotificationEventListener の処理を書きます。

ExampleController

<?php
namespace App\Controller;

use App\Repository\UserRepository;
use App\EventListener\NotificationEventListener;
use App\EventDispatcher;
use Kojirock5260\Component\Adapter\SlackAdapter;
use Kojirock5260\User\UseCase\RegisterUser;
use Kojirock5260\User\Exception\RegisterUserException;

class UserController extends AppController
{
    public function registerAction()
    {
        try {
            $slackConfig = Configure::read('Slack');
            $this->getEventManager()->on(new NotificationEventListener(new SlackAdapter($slackConfig)));
            $userId = (new RegisterUser(
                new UserRepository($this->UsersTable),
                new EventDispatcher($this->getEventManager())
            ))($this->request->getData('userData'));
            $this->Flash->error('新規登録ありがとうございます。');
        } catch (RegisterUserException $e) {
            $this->Flash->error('ユーザー登録が失敗しました。');
        }
    }
}

NotificationEventListener

<?php
namespace App\EventListener;

use Cake\Event\EventListenerInterface;

class NotificationEventListener implements EventListenerInterface
{
    private $slackAdapter;

    public function __construct(SlackAdapter $slackAdapter)
    {
        $this->slackAdapter = $slackAdapter;
    }

    public function implementedEvents(): array
    {
        return [
            'Notification.Slack' => 'notificationSlack',
        ];
    }

    public function notificationSlack($event, array $data): void
    {
        $this->slackAdapter->send('ユーザー新規登録!', $data['params']);
    }
}

本来はもっと複雑な処理があると思いますが、一旦はこんな感じにします。

この ExampleControllerをテストしたい場合、slackに通知するeventをmockしたくなります。

その場合は、以下のように書きます。

ExampleControllerTest.php

<?php
namespace App\Test\TestCase\Controller;

use Cake\Event\Event;
use Cake\Event\EventManager;
use Cake\TestSuite\IntegrationTestCase;

class UserControllerTest extends IntegrationTestCase
{
    /**
     * @test
     */
    public function register()
    {
        EventManager::instance()->on('Notification.Slack', function (Event $event) {
            $slackAdapterMock = $this->createPartialMock(SlackAdapter::class, ['send']);
            $slackAdapterMock->method('send')->willReturn(true);
            $event->getSubject()->slackAdapter = $slackAdapterMock;
        });

        $this->enableCsrfToken();
        $this->post('/user/register', [
            'email' => 'hogehoge@fuga.com',
                'name'  => 'kojirock'
        ]);

        $flashData = $this->_requestSession->read('Flash.flash.0');
        self::assertSame('Flash/success', $flashData['element']);
    }
}

これで、slackAdapterのsend()メソッドを実行せずそれ以外のコードが実行されるようになります。

終わりに

cakephp4にあげて、早くDIの恩恵を受けたいです。。 このテストコード関連でも、やっぱり外から注入できないとmockがとてもつらいことになってしまってて、大変です。

現場からは以上です。