*** Check out Grokking Magento ***

Vinai Kopp

Magento Expert, Developer & Trainer

  • 02. The Plugin Config Kata

    February 5, 2016

    Mage2Katas

    Welcome to the plugin config kata.

    In this episode we are going to configure a plugin that intercepts calls to methods of the CustomerRepositoryInterface class.
    To start we will intercept every call, regardless of scope. Then we will change our config so the plugin only is triggered only in the webapi_rest scope.

    And of course we will write tests first, so when they pass we know our configuration is correct.

    This is the blogpost for the second episode of the Screencast.


    We have three different ways to intercept methods in Magento 2

    • before
    • around
    • after

    For testing the configuration however they are the same.


    Lets get started with an integration test to check our plugin configuration will be correct.
    I’ll put the code into the file Mage2Kata\Interceptor\Test\Integration\Plugin\CustomerRepositoryPluginTest.php.

    <?php
     
    namespace Mage2Kata\Interceptor\Plugin;
     
    use Magento\Customer\Api\CustomerRepositoryInterface;
    use Magento\TestFramework\Interception\PluginList;
    use Magento\TestFramework\ObjectManager;
     
    class CustomerRepositoryPluginTest extends \PHPUnit_Framework_TestCase
    {
        public function testTheCustomerRepositoryPluginIsRegistered()
        {
            /** @var ObjectManager $objectManager */
            $objectManager = ObjectManager::getInstance();
     
            /** @var PluginList $pluginList */
            $pluginList = $objectManager->create(PluginList::class);
     
            $pluginInfo = $pluginList->get(CustomerRepositoryInterface::class, []);
            $this->assertSame(CustomerRepositoryPlugin::class, $pluginInfo['mage2kata_interceptor']['instance']);
        }
    }

    Please make sure you get the class imports right for the ObjectManager and the PluginList, as there are more then one class with that name in Magento 2.

    Note that the CustomerRepositoryPlugin class doesn’t yet exist. We can still write a test for it using the class constant. That is one of the reasons why it is so handy to use the same namespace for the class under test and the test class.

    Now lets add the interceptor configuration.

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <type name="Magento\Customer\Api\CustomerRepositoryInterface">
            <plugin name="mage2kata_interceptor" type="Mage2Kata\Interceptor\Plugin\CustomerRepositoryPlugin"/>
        </type>
    </config>

    Remember we need to flush the integration test configuration after adding an XML file if we disabled test cleanup.

    Indeed this configuration makes our test pass.

    Note that our plugin method will be called always, regardless which scope is currently being processed, because the di.xml is directly in the etc directory. Lets assume we want our plugin only to run in the webapi_rest or frontend area.

    Lets modify the test to ensure the plugin is called in the webapi_rest area.

    <?php
     
    namespace Mage2Kata\Interceptor\Plugin;
     
    use Magento\Customer\Api\CustomerRepositoryInterface;
    use Magento\Framework\App\Area;
    use Magento\TestFramework\App\State as AppState;
    use Magento\TestFramework\Interception\PluginList;
    use Magento\TestFramework\ObjectManager;
     
    class CustomerRepositoryPluginTest extends \PHPUnit_Framework_TestCase
    {
        public function testTheCustomerRepositoryPluginIsRegistered()
        {
            /** @var ObjectManager $objectManager */
            $objectManager = ObjectManager::getInstance();
     
            /** @var AppState $applicationState */
            $applicationState = $objectManager->get(AppState::class);
            $applicationState->setAreaCode(Area::AREA_WEBAPI_REST);
     
            /** @var PluginList $pluginList */
            $pluginList = $objectManager->create(PluginList::class);
     
            $pluginInfo = $pluginList->get(CustomerRepositoryInterface::class, []);
            $this->assertSame(CustomerRepositoryPlugin::class, $pluginInfo['mage2kata_interceptor']['instance']);
        }
    }

    For the frontend, adminhtml and global scope the test annotation @magentoAppArea can be used, but because that annotation currently causes the test framework to load the configured design area, it can not be used for any of the other webapi or cron areas.

    Of course this test is still passing, since our plugin is registered in the global scope, because of which it will be executed regardless of the current area.

    Lets add a test to let us change that.

    public function testTheCustomerRepositoryPluginIsNotRegisteredInGlobalScope()
    {
        /** @var ObjectManager $objectManager */
        $objectManager = ObjectManager::getInstance();
     
        /** @var AppState $applicationState */
        $applicationState = $objectManager->get(AppState::class);
        $applicationState->setAreaCode(Area::AREA_GLOBAL);
     
        /** @var PluginList $pluginList */
        $pluginList = $objectManager->create(PluginList::class);
     
        $pluginInfo = $pluginList->get(CustomerRepositoryInterface::class, []);
        $this->assertArrayNotHasKey('mage2kata_interceptor', $pluginInfo);
    }

    To make this test pass, we have to move the etc/di.xml file into the directory etc/webapi_rest/di.xml.
    Remember to clear the test config cache if you have disabled automatic test cleanup.

    Now that the tests are passing, we get to refactor a bit. Here is what I ended up with:

    <?php
     
    namespace Mage2Kata\Interceptor\Plugin;
     
    use Magento\Customer\Api\CustomerRepositoryInterface;
    use Magento\Framework\App\Area;
    use Magento\TestFramework\App\State as AppState;
    use Magento\TestFramework\Interception\PluginList;
    use Magento\TestFramework\ObjectManager;
     
    class CustomerRepositoryPluginTest extends \PHPUnit_Framework_TestCase
    {
        /**
         * @var ObjectManager
         */
        private $objectManager;
     
        /**
         * @param string $areaCode
         */
        private function setMagentoArea($areaCode)
        {
            /** @var AppState $applicationState */
            $applicationState = $this->objectManager->get(AppState::class);
            $applicationState->setAreaCode($areaCode);
        }
     
        protected function setUp()
        {
            $this->objectManager = ObjectManager::getInstance();
        }
     
        protected function tearDown()
        {
            $this->setMagentoArea(null);
        }
     
        public function testTheCustomerRepositoryPluginIsRegisteredInTheWebapiRestScope()
        {
            $this->setMagentoArea(Area::AREA_WEBAPI_REST);
     
            /** @var PluginList $pluginList */
            $pluginList = $this->objectManager->create(PluginList::class);
     
            $pluginInfo = $pluginList->get(CustomerRepositoryInterface::class, []);
            $this->assertSame(CustomerRepositoryPlugin::class, $pluginInfo['mage2kata_interceptor']['instance']);
        }
     
        public function testTheCustomerRepositoryPluginIsNotRegisteredInGlobalScope()
        {
            $this->setMagentoArea(Area::AREA_GLOBAL);
     
            /** @var PluginList $pluginList */
            $pluginList = $this->objectManager->create(PluginList::class);
     
            $pluginInfo = $pluginList->get(CustomerRepositoryInterface::class, []);
            $this->assertArrayNotHasKey('mage2kata_interceptor', $pluginInfo);
        }
    }

    Note that I included a tearDown method to reset the application state after each test, because it persists between tests otherwise.

    Otherwise I moved the object manager into a instance property, and extracted the setting of the application area into an utility method.
    I like to move utility methods above the setUp method.

    Now you can either delete the code if you are finished with the kata, or continue to the next episode, the “Around Interceptor Kata”, to see the implementation of the actual plugin.

    Please leave a feedback here by commenting on the blog, on Twitter, or by leaving a comment below the video on youtube.

    Thanks for reading!

    comments powered by Disqus

Read Grokking Magento now!

"The best technical reading I had since years by @VinaiKopp"

Tim Bezhashvyly
Magento Developer