diff --git a/app/code/Magento/Backend/App/Action/Plugin/MassactionKey.php b/app/code/Magento/Backend/App/Action/Plugin/MassactionKey.php
index e2ce581806f63f7870249ff20e7abbe14263185d..44cfe2bb17dc86ded0078af0b2ef000a87e97137 100644
--- a/app/code/Magento/Backend/App/Action/Plugin/MassactionKey.php
+++ b/app/code/Magento/Backend/App/Action/Plugin/MassactionKey.php
@@ -7,29 +7,27 @@
  */
 namespace Magento\Backend\App\Action\Plugin;
 
+use Magento\Framework\App\RequestInterface;
+use Magento\Backend\App\AbstractAction;
+
 class MassactionKey
 {
     /**
      * Process massaction key
      *
-     * @param \Magento\Backend\App\AbstractAction $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
+     * @param AbstractAction $subject
+     * @param RequestInterface $request
      *
-     * @return mixed
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
-        \Magento\Backend\App\AbstractAction $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
+    public function beforeDispatch(AbstractAction $subject, RequestInterface $request)
+    {
         $key = $request->getPost('massaction_prepare_key');
         if ($key) {
             $postData = $request->getPost($key);
             $value = is_array($postData) ? $postData : explode(',', $postData);
             $request->setPostValue($key, $value ? $value : null);
         }
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Backend/Test/Unit/App/Action/Plugin/MassactionKeyTest.php b/app/code/Magento/Backend/Test/Unit/App/Action/Plugin/MassactionKeyTest.php
index b615385f0546e031086e17f179b13e13d6d06cac..3f4af8c3259e95aebc109b3ca08a16babe5ee6db 100644
--- a/app/code/Magento/Backend/Test/Unit/App/Action/Plugin/MassactionKeyTest.php
+++ b/app/code/Magento/Backend/Test/Unit/App/Action/Plugin/MassactionKeyTest.php
@@ -6,6 +6,8 @@
 namespace Magento\Backend\Test\Unit\App\Action\Plugin;
 
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Backend\App\AbstractAction;
+use Magento\Framework\App\RequestInterface;
 
 class MassactionKeyTest extends \PHPUnit_Framework_TestCase
 {
@@ -15,17 +17,12 @@ class MassactionKeyTest extends \PHPUnit_Framework_TestCase
     protected $plugin;
 
     /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|RequestInterface
      */
     protected $requestMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|AbstractAction
      */
     protected $subjectMock;
 
@@ -35,27 +32,32 @@ class MassactionKeyTest extends \PHPUnit_Framework_TestCase
             return 'Expected';
         };
         $this->subjectMock = $this->getMock(\Magento\Backend\App\AbstractAction::class, [], [], '', false);
-        $this->requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false);
+        $this->requestMock = $this->getMockForAbstractClass(
+            RequestInterface::class,
+            [],
+            '',
+            false,
+            false,
+            true,
+            ['getPost', 'setPostValue']
+        );
 
         $objectManager = new ObjectManager($this);
         $this->plugin = $objectManager->getObject(
             \Magento\Backend\App\Action\Plugin\MassactionKey::class,
             [
                 'subject' => $this->subjectMock,
-                'closure' => $this->closureMock,
                 'request' => $this->requestMock,
             ]
         );
     }
 
     /**
-     * @covers \Magento\Backend\App\Action\Plugin\MassactionKey::aroundDispatch
-     *
      * @param $postData array|string
      * @param array $convertedData
-     * @dataProvider aroundDispatchDataProvider
+     * @dataProvider beforeDispatchDataProvider
      */
-    public function testAroundDispatchWhenMassactionPrepareKeyRequestExists($postData, $convertedData)
+    public function testBeforeDispatchWhenMassactionPrepareKeyRequestExists($postData, $convertedData)
     {
         $this->requestMock->expects($this->at(0))
             ->method('getPost')
@@ -69,13 +71,10 @@ class MassactionKeyTest extends \PHPUnit_Framework_TestCase
             ->method('setPostValue')
             ->with('key', $convertedData);
 
-        $this->assertEquals(
-            'Expected',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
-    public function aroundDispatchDataProvider()
+    public function beforeDispatchDataProvider()
     {
         return [
             'post_data_is_array' => [['key'], ['key']],
@@ -83,10 +82,7 @@ class MassactionKeyTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
-    /**
-     * @covers \Magento\Backend\App\Action\Plugin\MassactionKey::aroundDispatch
-     */
-    public function testAroundDispatchWhenMassactionPrepareKeyRequestNotExists()
+    public function testBeforeDispatchWhenMassactionPrepareKeyRequestNotExists()
     {
         $this->requestMock->expects($this->once())
             ->method('getPost')
@@ -95,9 +91,6 @@ class MassactionKeyTest extends \PHPUnit_Framework_TestCase
         $this->requestMock->expects($this->never())
             ->method('setPostValue');
 
-        $this->assertEquals(
-            'Expected',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 }
diff --git a/app/code/Magento/Bundle/Model/Plugin/QuoteItem.php b/app/code/Magento/Bundle/Model/Plugin/QuoteItem.php
index 8861d864b8180f0c11191a3014c5e49791263656..4009f6e3496bbb1ef6f463cac588dc15d416481b 100644
--- a/app/code/Magento/Bundle/Model/Plugin/QuoteItem.php
+++ b/app/code/Magento/Bundle/Model/Plugin/QuoteItem.php
@@ -5,34 +5,34 @@
  */
 namespace Magento\Bundle\Model\Plugin;
 
-use Closure;
+use Magento\Quote\Model\Quote\Item\ToOrderItem;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Quote\Model\Quote\Item\AbstractItem;
 
+/**
+ * Plugin for Magento\Quote\Model\Quote\Item\ToOrderItem
+ */
 class QuoteItem
 {
     /**
      * Add bundle attributes to order data
      *
-     * @param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
-     * @param callable $proceed
-     * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
-     * @param array $additional
-     * @return \Magento\Sales\Model\Order\Item
+     * @param ToOrderItem $subject
+     * @param OrderItemInterface $orderItem
+     * @param AbstractItem $item
+     * @param array $data
+     * @return OrderItemInterface
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundConvert(
-        \Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
-        Closure $proceed,
-        \Magento\Quote\Model\Quote\Item\AbstractItem $item,
-        $additional = []
-    ) {
-        /** @var $orderItem \Magento\Sales\Model\Order\Item */
-        $orderItem = $proceed($item, $additional);
-
+    public function afterConvert(ToOrderItem $subject, OrderItemInterface $orderItem, AbstractItem $item, $data = [])
+    {
         if ($attributes = $item->getProduct()->getCustomOption('bundle_selection_attributes')) {
             $productOptions = $orderItem->getProductOptions();
             $productOptions['bundle_selection_attributes'] = $attributes->getValue();
             $orderItem->setProductOptions($productOptions);
         }
+
         return $orderItem;
     }
 }
diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Plugin/QuoteItemTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Plugin/QuoteItemTest.php
index 6feb4472aaa9a938165f16eb1f37b9523246bad5..03bac48a71be10d28343a935c3e198363003e69e 100644
--- a/app/code/Magento/Bundle/Test/Unit/Model/Plugin/QuoteItemTest.php
+++ b/app/code/Magento/Bundle/Test/Unit/Model/Plugin/QuoteItemTest.php
@@ -5,42 +5,66 @@
  */
 namespace Magento\Bundle\Test\Unit\Model\Plugin;
 
+use Magento\Quote\Model\Quote\Item\ToOrderItem;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Quote\Model\Quote\Item\AbstractItem;
+use Magento\Catalog\Model\Product;
+
 class QuoteItemTest extends \PHPUnit_Framework_TestCase
 {
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|Product
+     */
+    private $productMock;
+
     /** @var \Magento\Bundle\Model\Plugin\QuoteItem */
     protected $model;
 
-    /** @var \PHPUnit_Framework_MockObject_MockObject */
+    /** @var \PHPUnit_Framework_MockObject_MockObject|AbstractItem */
     protected $quoteItemMock;
 
-    /** @var \PHPUnit_Framework_MockObject_MockObject */
+    /** @var \PHPUnit_Framework_MockObject_MockObject|OrderItemInterface */
     protected $orderItemMock;
 
     /**
-     * @var /PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|ToOrderItem
      */
     protected $subjectMock;
 
-    /**
-     * @var /Closure
-     */
-    protected $closureMock;
-
     protected function setUp()
     {
-        $this->orderItemMock = $this->getMock(\Magento\Sales\Model\Order\Item::class, [], [], '', false);
-        $this->quoteItemMock = $this->getMock(\Magento\Quote\Model\Quote\Item::class, [], [], '', false);
-        $orderItem = $this->orderItemMock;
-        $this->closureMock = function () use ($orderItem) {
-            return $orderItem;
-        };
-        $this->subjectMock = $this->getMock(\Magento\Quote\Model\Quote\Item\ToOrderItem::class, [], [], '', false);
+        $this->orderItemMock = $this->getMockForAbstractClass(
+            OrderItemInterface::class,
+            [],
+            '',
+            false,
+            false,
+            true,
+            ['getProductOptions', 'setProductOptions']
+        );
+        $this->quoteItemMock = $this->getMockForAbstractClass(
+            AbstractItem::class,
+            [],
+            '',
+            false,
+            false,
+            true,
+            ['getProduct']
+        );
+        $this->subjectMock = $this->getMock(ToOrderItem::class, [], [], '', false);
+        $this->productMock = $this->getMock(Product::class, [], [], '', false);
         $this->model = new \Magento\Bundle\Model\Plugin\QuoteItem();
     }
 
     public function testAroundItemToOrderItemPositive()
     {
-        $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false);
+        $attributeValue = 'test_value';
+        $productOptions = [
+            'option_1' => 'value_1',
+            'option_2' => 'value_2'
+        ];
+        $expectedOptions = $productOptions + ['bundle_selection_attributes' => $attributeValue];
+
         $bundleAttribute = $this->getMock(
             \Magento\Catalog\Model\Product\Configuration\Item\Option::class,
             [],
@@ -48,38 +72,34 @@ class QuoteItemTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $productMock->expects(
-            $this->once()
-        )->method(
-            'getCustomOption'
-        )->with(
-            'bundle_selection_attributes'
-        )->will(
-            $this->returnValue($bundleAttribute)
-        );
-        $this->quoteItemMock->expects($this->once())->method('getProduct')->will($this->returnValue($productMock));
-        $this->orderItemMock->expects($this->once())->method('setProductOptions');
+        $bundleAttribute->expects($this->once())
+            ->method('getValue')
+            ->willReturn($attributeValue);
 
-        $orderItem = $this->model->aroundConvert($this->subjectMock, $this->closureMock, $this->quoteItemMock, []);
+        $this->productMock->expects($this->once())
+            ->method('getCustomOption')
+            ->with('bundle_selection_attributes')
+            ->willReturn($bundleAttribute);
+        $this->quoteItemMock->expects($this->once())->method('getProduct')->willReturn($this->productMock);
+
+        $this->orderItemMock->expects($this->once())->method('getProductOptions')->willReturn($productOptions);
+        $this->orderItemMock->expects($this->once())->method('setProductOptions')->with($expectedOptions);
+
+        $orderItem = $this->model->afterConvert($this->subjectMock, $this->orderItemMock, $this->quoteItemMock);
         $this->assertSame($this->orderItemMock, $orderItem);
     }
 
     public function testAroundItemToOrderItemNegative()
     {
-        $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false);
-        $productMock->expects(
-            $this->once()
-        )->method(
-            'getCustomOption'
-        )->with(
-            'bundle_selection_attributes'
-        )->will(
-            $this->returnValue(false)
-        );
-        $this->quoteItemMock->expects($this->once())->method('getProduct')->will($this->returnValue($productMock));
+        $this->productMock->expects($this->once())
+            ->method('getCustomOption')
+            ->with('bundle_selection_attributes')->willReturn(false);
+
+        $this->quoteItemMock->expects($this->once())->method('getProduct')
+            ->willReturn($this->productMock);
         $this->orderItemMock->expects($this->never())->method('setProductOptions');
 
-        $orderItem = $this->model->aroundConvert($this->subjectMock, $this->closureMock, $this->quoteItemMock, []);
+        $orderItem = $this->model->afterConvert($this->subjectMock, $this->orderItemMock, $this->quoteItemMock);
         $this->assertSame($this->orderItemMock, $orderItem);
     }
 }
diff --git a/app/code/Magento/Catalog/Model/CatalogRegistry.php b/app/code/Magento/Catalog/Model/CatalogRegistry.php
deleted file mode 100644
index 7eb7b3f77f8b1d614d5455f455ee229440bcef9e..0000000000000000000000000000000000000000
--- a/app/code/Magento/Catalog/Model/CatalogRegistry.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-/**
- * Copyright © 2016 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\Catalog\Model;
-
-use Magento\Framework\Model\EntityRegistry;
-use Magento\Framework\EntityManager\EntityManager;
-
-/**
- * Class CatalogRegistry
- */
-class CatalogRegistry
-{
-    /**
-     * @var EntityRegistry
-     */
-    protected $entityRegistry;
-
-    /**
-     * CatalogRegistry constructor.
-     *
-     * @param EntityRegistry $entityRegistry
-     */
-    public function __construct(
-        EntityRegistry $entityRegistry
-    ) {
-        $this->entityRegistry = $entityRegistry;
-    }
-
-    /**
-     * @param EntityManager $subject
-     * @param \Closure $proceed
-     * @param string $entityType
-     * @param object $entity
-     * @param string $identifier
-     * @return null|object
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
-     */
-    public function aroundLoad(EntityManager $subject, \Closure $proceed, $entityType, $entity, $identifier)
-    {
-        $object = $this->entityRegistry->retrieve($entityType, $identifier);
-        if (!$object) {
-            $object = $proceed($entityType, $entity, $identifier);
-            $this->entityRegistry->register($entityType, $identifier, $object);
-        }
-        return $object;
-    }
-}
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/IndexerConfigData.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/IndexerConfigData.php
index ae796a8b979fc402d6e6722dde06f8f50b94aeec..3b8061d2f93387ffdb15cf65dbee9193ce3964a7 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/IndexerConfigData.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/IndexerConfigData.php
@@ -5,17 +5,20 @@
  */
 namespace Magento\Catalog\Model\Indexer\Category\Flat\Plugin;
 
+use Magento\Indexer\Model\Config\Data;
+use Magento\Catalog\Model\Indexer\Category\Flat\State;
+
 class IndexerConfigData
 {
     /**
-     * @var \Magento\Catalog\Model\Indexer\Category\Flat\State
+     * @var State
      */
     protected $state;
 
     /**
-     * @param \Magento\Catalog\Model\Indexer\Category\Flat\State $state
+     * @param State $state
      */
-    public function __construct(\Magento\Catalog\Model\Indexer\Category\Flat\State $state)
+    public function __construct(State $state)
     {
         $this->state = $state;
     }
@@ -23,24 +26,18 @@ class IndexerConfigData
     /**
      *  Unset indexer data in configuration if flat is disabled
      *
-     * @param \Magento\Indexer\Model\Config\Data $subject
-     * @param callable $proceed
+     * @param Data $subject
+     * @param mixed $data
      * @param string $path
      * @param mixed $default
      *
      * @return mixed
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundGet(
-        \Magento\Indexer\Model\Config\Data $subject,
-        \Closure $proceed,
-        $path = null,
-        $default = null
-    ) {
-        $data = $proceed($path, $default);
-
+    public function afterGet(Data $subject, $data, $path = null, $default = null)
+    {
         if (!$this->state->isFlatEnabled()) {
-            $indexerId = \Magento\Catalog\Model\Indexer\Category\Flat\State::INDEXER_ID;
+            $indexerId = State::INDEXER_ID;
             if (!$path && isset($data[$indexerId])) {
                 unset($data[$indexerId]);
             } elseif ($path) {
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/StoreGroup.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/StoreGroup.php
index 7c1c87278fb7184817f75af47f56ca084765431e..502bb8b542f4efaa122bf341548615b93a4fa751 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/StoreGroup.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Plugin/StoreGroup.php
@@ -5,24 +5,34 @@
  */
 namespace Magento\Catalog\Model\Indexer\Category\Flat\Plugin;
 
+use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Catalog\Model\Indexer\Category\Flat\State;
+
 class StoreGroup
 {
-    /** @var \Magento\Framework\Indexer\IndexerRegistry */
+    /**
+     * @var bool
+     */
+    private $needInvalidating;
+
+    /**
+     * @var IndexerRegistry
+     */
     protected $indexerRegistry;
 
     /**
-     * @var \Magento\Catalog\Model\Indexer\Category\Flat\State
+     * @var State
      */
     protected $state;
 
     /**
-     * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
-     * @param \Magento\Catalog\Model\Indexer\Category\Flat\State $state
+     * @param IndexerRegistry $indexerRegistry
+     * @param State $state
      */
-    public function __construct(
-        \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
-        \Magento\Catalog\Model\Indexer\Category\Flat\State $state
-    ) {
+    public function __construct(IndexerRegistry $indexerRegistry, State $state)
+    {
         $this->indexerRegistry = $indexerRegistry;
         $this->state = $state;
     }
@@ -30,31 +40,41 @@ class StoreGroup
     /**
      * Validate changes for invalidating indexer
      *
-     * @param \Magento\Framework\Model\AbstractModel $group
+     * @param AbstractModel $group
      * @return bool
      */
-    protected function validate(\Magento\Framework\Model\AbstractModel $group)
+    protected function validate(AbstractModel $group)
     {
         return $group->dataHasChangedFor('root_category_id') && !$group->isObjectNew();
     }
 
     /**
-     * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\Model\AbstractModel $group
+     * Check if need invalidate flat category indexer
      *
-     * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+     * @param AbstractDb $subject
+     * @param AbstractModel $group
+     *
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $group
-    ) {
-        $needInvalidating = $this->validate($group);
-        $objectResource = $proceed($group);
-        if ($needInvalidating && $this->state->isFlatEnabled()) {
-            $this->indexerRegistry->get(\Magento\Catalog\Model\Indexer\Category\Flat\State::INDEXER_ID)->invalidate();
+    public function beforeSave(AbstractDb $subject, AbstractModel $group)
+    {
+        $this->needInvalidating = $this->validate($group);
+    }
+
+    /**
+     * Invalidate flat category indexer if root category changed for store group
+     *
+     * @param AbstractDb $subject
+     * @param AbstractDb $objectResource
+     *
+     * @return AbstractDb
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterSave(AbstractDb $subject, AbstractDb $objectResource)
+    {
+        if ($this->needInvalidating && $this->state->isFlatEnabled()) {
+            $this->indexerRegistry->get(State::INDEXER_ID)->invalidate();
         }
 
         return $objectResource;
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php
index dce748c34fa3fc9e8534af16b895df94d92f9f40..f81c0165b638a2034a1c57209a8959cb404d49a2 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Plugin/StoreGroup.php
@@ -5,35 +5,58 @@
  */
 namespace Magento\Catalog\Model\Indexer\Category\Product\Plugin;
 
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Catalog\Model\Indexer\Category\Product;
+
 class StoreGroup
 {
-    /** @var \Magento\Framework\Indexer\IndexerRegistry */
+    /**
+     * @var bool
+     */
+    private $needInvalidating;
+
+    /**
+     * @var IndexerRegistry
+     */
     protected $indexerRegistry;
 
     /**
-     * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
+     * @param IndexerRegistry $indexerRegistry
      */
-    public function __construct(\Magento\Framework\Indexer\IndexerRegistry $indexerRegistry)
+    public function __construct(IndexerRegistry $indexerRegistry)
     {
         $this->indexerRegistry = $indexerRegistry;
     }
 
     /**
-     * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\Model\AbstractModel $group
-     * @return mixed
+     * Check if need invalidate flat category indexer
+     *
+     * @param AbstractDb $subject
+     * @param AbstractModel $group
+     *
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $group
-    ) {
-        $needInvalidating = $this->validate($group);
-        $objectResource = $proceed($group);
-        if ($needInvalidating) {
-            $this->indexerRegistry->get(\Magento\Catalog\Model\Indexer\Category\Product::INDEXER_ID)->invalidate();
+    public function beforeSave(AbstractDb $subject, AbstractModel $group)
+    {
+        $this->needInvalidating = $this->validate($group);
+    }
+
+    /**
+     * Invalidate flat product
+     *
+     * @param AbstractDb $subject
+     * @param AbstractDb $objectResource
+     *
+     * @return AbstractDb
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterSave(AbstractDb $subject, AbstractDb $objectResource)
+    {
+        if ($this->needInvalidating) {
+            $this->indexerRegistry->get(Product::INDEXER_ID)->invalidate();
         }
 
         return $objectResource;
@@ -42,15 +65,12 @@ class StoreGroup
     /**
      * Validate changes for invalidating indexer
      *
-     * @param \Magento\Framework\Model\AbstractModel $group
+     * @param AbstractModel $group
      * @return bool
      */
-    protected function validate(\Magento\Framework\Model\AbstractModel $group)
+    protected function validate(AbstractModel $group)
     {
-        return ($group->dataHasChangedFor(
-            'website_id'
-        ) || $group->dataHasChangedFor(
-            'root_category_id'
-        )) && !$group->isObjectNew();
+        return ($group->dataHasChangedFor('website_id') || $group->dataHasChangedFor('root_category_id'))
+               && !$group->isObjectNew();
     }
 }
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Plugin/AttributeSet.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Plugin/AttributeSet.php
index e9119405143444984bc7895fecc04909aeb19fed..66251853e2bbb8e147ffa21541634820d0e388cd 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Plugin/AttributeSet.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Plugin/AttributeSet.php
@@ -5,10 +5,25 @@
  */
 namespace Magento\Catalog\Model\Indexer\Product\Eav\Plugin;
 
+use Magento\Eav\Model\Entity\Attribute\Set as EavAttributeSet;
+use Magento\Catalog\Model\Indexer\Product\Eav\Processor;
+use Magento\Eav\Model\Entity\Attribute\SetFactory;
+use Magento\Framework\App\ObjectManager;
+
 class AttributeSet
 {
     /**
-     * @var \Magento\Catalog\Model\Indexer\Product\Eav\Processor
+     * @var bool
+     */
+    private $requiresReindex;
+
+    /**
+     * @var SetFactory
+     */
+    private $setFactory;
+
+    /**
+     * @var Processor
      */
     protected $_indexerEavProcessor;
 
@@ -18,43 +33,66 @@ class AttributeSet
     protected $_attributeFilter;
 
     /**
-     * @param \Magento\Catalog\Model\Indexer\Product\Eav\Processor $indexerEavProcessor
+     * @param Processor $indexerEavProcessor
      * @param AttributeSet\IndexableAttributeFilter $filter
      */
-    public function __construct(
-        \Magento\Catalog\Model\Indexer\Product\Eav\Processor $indexerEavProcessor,
-        AttributeSet\IndexableAttributeFilter $filter
-    ) {
+    public function __construct(Processor $indexerEavProcessor, AttributeSet\IndexableAttributeFilter $filter)
+    {
         $this->_indexerEavProcessor = $indexerEavProcessor;
         $this->_attributeFilter = $filter;
     }
 
     /**
-     * Invalidate EAV indexer if attribute set has indexable attributes changes
+     * Return attribute set factory
      *
-     * @param \Magento\Eav\Model\Entity\Attribute\Set $subject
-     * @param callable $proceed
+     * @return SetFactory
+     * @deprecated
+     */
+    private function getAttributeSetFactory()
+    {
+        if ($this->setFactory === null) {
+            $this->setFactory = ObjectManager::getInstance()->get(SetFactory::class);
+        }
+        return $this->setFactory;
+    }
+
+    /**
+     * Check whether is needed to invalidate EAV indexer
      *
-     * @return \Magento\Eav\Model\Entity\Attribute\Set
+     * @param EavAttributeSet $subject
      *
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @return void
      */
-    public function aroundSave(\Magento\Eav\Model\Entity\Attribute\Set $subject, \Closure $proceed)
+    public function beforeSave(EavAttributeSet $subject)
     {
-        $requiresReindex = false;
+        $this->requiresReindex = false;
         if ($subject->getId()) {
-            /** @var \Magento\Eav\Model\Entity\Attribute\Set $originalSet */
-            $originalSet = clone $subject;
+            /** @var EavAttributeSet $originalSet */
+            $originalSet = $this->getAttributeSetFactory()->create();
             $originalSet->initFromSkeleton($subject->getId());
             $originalAttributeCodes = array_flip($this->_attributeFilter->filter($originalSet));
-            $subjectAttributeCodes = array_flip($this->_attributeFilter->filter($subject));
-            $requiresReindex = (bool)count(array_merge(
-                array_diff_key($subjectAttributeCodes, $originalAttributeCodes),
-                array_diff_key($originalAttributeCodes, $subjectAttributeCodes)
-            ));
+            $subjectAttributeCodes  = array_flip($this->_attributeFilter->filter($subject));
+            $this->requiresReindex  = (bool)count(
+                array_merge(
+                    array_diff_key($subjectAttributeCodes, $originalAttributeCodes),
+                    array_diff_key($originalAttributeCodes, $subjectAttributeCodes)
+                )
+            );
         }
-        $result = $proceed();
-        if ($requiresReindex) {
+    }
+
+    /**
+     * Invalidate EAV indexer if attribute set has indexable attributes changes
+     *
+     * @param EavAttributeSet $subject
+     * @param EavAttributeSet $result
+     * @return EavAttributeSet
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterSave(EavAttributeSet $subject, EavAttributeSet $result)
+    {
+        if ($this->requiresReindex) {
             $this->_indexerEavProcessor->markIndexerAsInvalid();
         }
         return $result;
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php
index 60394766f4b529d28a977dd1453c4a3539d8f49c..0777e9a06e3484ac2e1cd7ac586207603a81b8b2 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php
@@ -5,51 +5,62 @@
  */
 namespace Magento\Catalog\Model\Indexer\Product\Flat\Plugin;
 
+use Magento\Catalog\Model\Indexer\Product\Flat\State as ProductFlatIndexerState;
+use Magento\Indexer\Model\Config\Data as ConfigData;
+use Magento\Catalog\Model\Indexer\Product\Flat\Processor as ProductFlatIndexerProcessor;
+
+/**
+ * Plugin for Magento\Indexer\Model\Config\Data
+ */
 class IndexerConfigData
 {
     /**
-     * @var \Magento\Catalog\Model\Indexer\Product\Flat\State
+     * @var ProductFlatIndexerState
      */
-    protected $_state;
+    protected $state;
 
     /**
-     * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $state
+     * @param ProductFlatIndexerState $state
      */
-    public function __construct(\Magento\Catalog\Model\Indexer\Product\Flat\State $state)
+    public function __construct(ProductFlatIndexerState $state)
     {
-        $this->_state = $state;
+        $this->state = $state;
     }
 
     /**
-     * Around get handler
+     * Modify returned config when flat indexer is disabled
      *
-     * @param \Magento\Indexer\Model\Config\Data $subject
-     * @param callable $proceed
+     * @param ConfigData $subject
+     * @param mixed $data
      * @param string $path
      * @param string $default
+     * @return mixed
      *
-     * @return mixed|null
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      *
      */
-    public function aroundGet(
-        \Magento\Indexer\Model\Config\Data $subject,
-        \Closure $proceed,
-        $path = null,
-        $default = null
-    ) {
-        $data = $proceed($path, $default);
-
-        if (!$this->_state->isFlatEnabled()) {
-            $indexerId = \Magento\Catalog\Model\Indexer\Product\Flat\Processor::INDEXER_ID;
-            if (!$path && isset($data[$indexerId])) {
-                unset($data[$indexerId]);
-            } elseif ($path) {
-                list($firstKey,) = explode('/', $path);
-                if ($firstKey == $indexerId) {
-                    $data = $default;
-                }
-            }
+    public function afterGet(ConfigData $subject, $data, $path = null, $default = null)
+    {
+        if ($this->state->isFlatEnabled()) {
+            return $data;
+        }
+
+        $indexerId = ProductFlatIndexerProcessor::INDEXER_ID;
+
+        if (!$path && isset($data[$indexerId])) {
+            unset($data[$indexerId]);
+
+            return $data;
+        }
+
+        if (!$path) {
+            return $data;
+        }
+
+        list($firstKey) = explode('/', $path);
+
+        if ($firstKey == $indexerId) {
+            $data = $default;
         }
 
         return $data;
diff --git a/app/code/Magento/Catalog/Model/Plugin/QuoteItemProductOption.php b/app/code/Magento/Catalog/Model/Plugin/QuoteItemProductOption.php
index 206acf7bf0f9595b9cdd4e827accd0fa2b70d530..7c8249b728cab55dd1d607dc307970a0e79abff3 100644
--- a/app/code/Magento/Catalog/Model/Plugin/QuoteItemProductOption.php
+++ b/app/code/Magento/Catalog/Model/Plugin/QuoteItemProductOption.php
@@ -5,44 +5,54 @@
  */
 namespace Magento\Catalog\Model\Plugin;
 
+use Magento\Quote\Model\Quote\Item\ToOrderItem as QuoteToOrderItem;
+use Magento\Quote\Model\Quote\Item\AbstractItem as AbstractQuoteItem;
+use Magento\Catalog\Model\Product\Option as ProductOption;
+
+/**
+ * Plugin for Magento\Quote\Model\Quote\Item\ToOrderItem
+ */
 class QuoteItemProductOption
 {
     /**
-     * @param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
-     * @param callable $proceed
-     * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
-     * @param array $additional
-     * @return \Magento\Sales\Model\Order\Item
+     * Perform preparations for custom options
+     *
+     * @param QuoteToOrderItem $subject
+     * @param AbstractQuoteItem $quoteItem
+     * @param array $data
+     * @return void
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundConvert(
-        \Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
-        \Closure $proceed,
-        \Magento\Quote\Model\Quote\Item\AbstractItem $item,
-        $additional = []
+    public function beforeConvert(
+        QuoteToOrderItem $subject,
+        AbstractQuoteItem $quoteItem,
+        $data = []
     ) {
-        /** @var $orderItem \Magento\Sales\Model\Order\Item */
-        $orderItem = $proceed($item, $additional);
+        if (!is_array($quoteItem->getOptions())) {
+            return;
+        }
+
+        foreach ($quoteItem->getOptions() as $itemOption) {
+            $code = explode('_', $itemOption->getCode());
+
+            if (!isset($code[1]) || !is_numeric($code[1])) {
+                continue;
+            }
+
+            $option = $quoteItem->getProduct()->getOptionById($code[1]);
+
+            if (!$option || $option->getType() != ProductOption::OPTION_TYPE_FILE) {
+                continue;
+            }
 
-        if (is_array($item->getOptions())) {
-            foreach ($item->getOptions() as $itemOption) {
-                $code = explode('_', $itemOption->getCode());
-                if (isset($code[1]) && is_numeric($code[1])) {
-                    $option = $item->getProduct()->getOptionById($code[1]);
-                    if ($option && $option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_FILE) {
-                        try {
-                            $option->groupFactory(
-                                $option->getType()
-                            )->setQuoteItemOption(
-                                $itemOption
-                            )->copyQuoteToOrder();
-                        } catch (\Exception $e) {
-                            continue;
-                        }
-                    }
-                }
+            try {
+                $option->groupFactory($option->getType())
+                    ->setQuoteItemOption($itemOption)
+                    ->copyQuoteToOrder();
+            } catch (\Exception $e) {
+                continue;
             }
         }
-        return $orderItem;
     }
 }
diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Attribute/Save.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Attribute/Save.php
index 1f1f4d3fdb2e2e97a7a073ab46f1f365d0c953b7..b7d77f40f6899dc99f0baf3efacbd72d56b99594 100644
--- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Attribute/Save.php
+++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Attribute/Save.php
@@ -6,44 +6,43 @@
 
 namespace Magento\Catalog\Plugin\Model\ResourceModel\Attribute;
 
+use Magento\Catalog\Model\ResourceModel\Attribute;
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\Cache\TypeListInterface;
+
 class Save
 {
     /**
-     * @var \Magento\PageCache\Model\Config
+     * @var Config
      */
     protected $config;
 
     /**
-     * @var \Magento\Framework\App\Cache\TypeListInterface
+     * @var TypeListInterface
      */
     protected $typeList;
 
     /**
-     * @param \Magento\PageCache\Model\Config $config
-     * @param \Magento\Framework\App\Cache\TypeListInterface $typeList
+     * @param Config $config
+     * @param TypeListInterface $typeList
      */
-    public function __construct(
-        \Magento\PageCache\Model\Config $config,
-        \Magento\Framework\App\Cache\TypeListInterface $typeList
-    ) {
+    public function __construct(Config $config, TypeListInterface $typeList)
+    {
         $this->config = $config;
         $this->typeList = $typeList;
     }
 
     /**
-     * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\Model\AbstractModel $attribute
-     * @return mixed
+     * Invalidate full page cache after saving attribute
+     *
+     * @param Attribute $subject
+     * @param Attribute $result
+     * @return Attribute $result
      *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Catalog\Model\ResourceModel\Attribute $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $attribute
-    ) {
-        $result = $proceed($attribute);
+    public function afterSave(Attribute $subject, Attribute $result)
+    {
         if ($this->config->isEnabled()) {
             $this->typeList->invalidate('full_page');
         }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/IndexerConfigDataTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/IndexerConfigDataTest.php
index 4cc35fd0cafb54f7fe78ef10be77b449ad0b6d5e..cde2365e21b6b649a5b3cb46a2221a3b728ea58e 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/IndexerConfigDataTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/IndexerConfigDataTest.php
@@ -5,36 +5,33 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Category\Flat\Plugin;
 
+use Magento\Catalog\Model\Indexer\Category\Flat\Plugin\IndexerConfigData;
+use Magento\Catalog\Model\Indexer\Category\Flat\State;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Indexer\Model\Config\Data;
+
 class IndexerConfigDataTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Catalog\Model\Indexer\Category\Flat\Plugin\IndexerConfigData
+     * @var IndexerConfigData
      */
     protected $model;
 
     /**
-     * @var \Magento\Catalog\Model\Indexer\Category\Flat\State|\PHPUnit_Framework_MockObject_MockObject
+     * @var State|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $stateMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var Data|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $subjectMock;
 
     protected function setUp()
     {
-        $this->stateMock = $this->getMock(
-            \Magento\Catalog\Model\Indexer\Category\Flat\State::class,
-            ['isFlatEnabled'],
-            [],
-            '',
-            false
-        );
-
-        $this->subjectMock = $this->getMock(\Magento\Indexer\Model\Config\Data::class, [], [], '', false);
-
-        $this->model = new \Magento\Catalog\Model\Indexer\Category\Flat\Plugin\IndexerConfigData($this->stateMock);
+        $this->stateMock = $this->getMock(State::class, ['isFlatEnabled'], [], '', false);
+        $this->subjectMock = $this->getMock(Data::class, [], [], '', false);
+        $this->model = (new ObjectManager($this))->getObject(IndexerConfigData::class, ['state' => $this->stateMock]);
     }
 
     /**
@@ -48,10 +45,7 @@ class IndexerConfigDataTest extends \PHPUnit_Framework_TestCase
     public function testAroundGet($isFlat, $path, $default, $inputData, $outputData)
     {
         $this->stateMock->expects($this->once())->method('isFlatEnabled')->will($this->returnValue($isFlat));
-        $closureMock = function () use ($inputData) {
-            return $inputData;
-        };
-        $this->assertEquals($outputData, $this->model->aroundGet($this->subjectMock, $closureMock, $path, $default));
+        $this->assertEquals($outputData, $this->model->afterGet($this->subjectMock, $inputData, $path, $default));
     }
 
     public function aroundGetDataProvider()
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreGroupTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreGroupTest.php
index 32d7e19e6d46e9f04730c3162c9aea8833d7b091..9757f0aef26aaed6db2aa143b521b5bb53d94c94 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreGroupTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreGroupTest.php
@@ -5,17 +5,23 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Category\Flat\Plugin;
 
-use \Magento\Catalog\Model\Indexer\Category\Flat\Plugin\StoreGroup;
+use Magento\Catalog\Model\Indexer\Category\Flat\Plugin\StoreGroup;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Catalog\Model\Indexer\Category\Flat\State;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Store\Model\ResourceModel\Group;
+use Magento\Store\Model\Group as GroupModel;
 
 class StoreGroupTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var \PHPUnit_Framework_MockObject_MockObject|IndexerInterface
      */
     protected $indexerMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Indexer\Category\Flat\State
+     * @var \PHPUnit_Framework_MockObject_MockObject|State
      */
     protected $stateMock;
 
@@ -25,29 +31,24 @@ class StoreGroupTest extends \PHPUnit_Framework_TestCase
     protected $model;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|Group
      */
     protected $subjectMock;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $indexerRegistryMock;
 
     /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|GroupModel
      */
     protected $groupMock;
 
     protected function setUp()
     {
         $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
+            IndexerInterface::class,
             [],
             '',
             false,
@@ -55,78 +56,64 @@ class StoreGroupTest extends \PHPUnit_Framework_TestCase
             true,
             ['getId', 'getState', '__wakeup']
         );
-        $this->stateMock = $this->getMock(
-            \Magento\Catalog\Model\Indexer\Category\Flat\State::class,
-            ['isFlatEnabled'],
-            [],
-            '',
-            false
-        );
-        $this->subjectMock = $this->getMock(\Magento\Store\Model\ResourceModel\Group::class, [], [], '', false);
+        $this->stateMock = $this->getMock(State::class, ['isFlatEnabled'], [], '', false);
+        $this->subjectMock = $this->getMock(Group::class, [], [], '', false);
 
         $this->groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
+            GroupModel::class,
             ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
             [],
             '',
             false
         );
-        $this->closureMock = function () {
-            return false;
-        };
 
         $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
+            IndexerRegistry::class,
             ['get'],
             [],
             '',
             false
         );
 
-        $this->model = new StoreGroup($this->indexerRegistryMock, $this->stateMock);
+        $this->model = (new ObjectManager($this))
+            ->getObject(
+                StoreGroup::class,
+                ['indexerRegistry' => $this->indexerRegistryMock, 'state' => $this->stateMock]
+            );
     }
 
-    public function testAroundSave()
+    public function testBeforeAndAfterSave()
     {
-        $this->stateMock->expects($this->once())->method('isFlatEnabled')->will($this->returnValue(true));
+        $this->stateMock->expects($this->once())->method('isFlatEnabled')->willReturn(true);
         $this->indexerMock->expects($this->once())->method('invalidate');
         $this->indexerRegistryMock->expects($this->once())
             ->method('get')
-            ->with(\Magento\Catalog\Model\Indexer\Category\Flat\State::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
-        $this->groupMock->expects(
-            $this->once()
-        )->method(
-            'dataHasChangedFor'
-        )->with(
-            'root_category_id'
-        )->will(
-            $this->returnValue(true)
+            ->with(State::INDEXER_ID)
+            ->willReturn($this->indexerMock);
+        $this->groupMock->expects($this->once())
+            ->method('dataHasChangedFor')
+            ->with('root_category_id')
+            ->willReturn(true);
+        $this->groupMock->expects($this->once())->method('isObjectNew')->willReturn(false);
+        $this->model->beforeSave($this->subjectMock, $this->groupMock);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock, $this->groupMock)
         );
-        $this->groupMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(false));
-        $this->assertFalse($this->model->aroundSave($this->subjectMock, $this->closureMock, $this->groupMock));
     }
 
-    public function testAroundSaveNotNew()
+    public function testBeforeAndAfterSaveNotNew()
     {
         $this->stateMock->expects($this->never())->method('isFlatEnabled');
-        $this->groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $this->groupMock->expects(
-            $this->once()
-        )->method(
-            'dataHasChangedFor'
-        )->with(
-            'root_category_id'
-        )->will(
-            $this->returnValue(true)
+        $this->groupMock->expects($this->once())
+            ->method('dataHasChangedFor')
+            ->with('root_category_id')
+            ->willReturn(true);
+        $this->groupMock->expects($this->once())->method('isObjectNew')->willReturn(true);
+        $this->model->beforeSave($this->subjectMock, $this->groupMock);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock, $this->groupMock)
         );
-        $this->groupMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(true));
-        $this->assertFalse($this->model->aroundSave($this->subjectMock, $this->closureMock, $this->groupMock));
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreViewTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreViewTest.php
index 21d3403c775a540d1329db3815f3e87198ee06c8..88d2d7c3e394c51205c1eb33f8e2c59341003258 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreViewTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Flat/Plugin/StoreViewTest.php
@@ -29,11 +29,6 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
      */
     protected $indexerRegistryMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -57,9 +52,6 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->closureMock = function () {
-            return false;
-        };
         $this->subjectMock = $this->getMock(\Magento\Store\Model\ResourceModel\Store::class, [], [], '', false);
         $this->indexerRegistryMock = $this->getMock(
             \Magento\Framework\Indexer\IndexerRegistry::class,
@@ -71,7 +63,7 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
         $this->model = new StoreView($this->indexerRegistryMock, $this->stateMock);
     }
 
-    public function testAroundSaveNewObject()
+    public function testBeforeAndAfterSaveNewObject()
     {
         $this->mockConfigFlatEnabled();
         $this->mockIndexerMethods();
@@ -83,10 +75,14 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
             false
         );
         $storeMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(true));
-        $this->assertFalse($this->model->aroundSave($this->subjectMock, $this->closureMock, $storeMock));
+        $this->model->beforeSave($this->subjectMock, $storeMock);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock, $storeMock)
+        );
     }
 
-    public function testAroundSaveHasChanged()
+    public function testBeforeAndAfterSaveHasChanged()
     {
         $storeMock = $this->getMock(
             \Magento\Store\Model\Store::class,
@@ -95,10 +91,14 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->assertFalse($this->model->aroundSave($this->subjectMock, $this->closureMock, $storeMock));
+        $this->model->beforeSave($this->subjectMock, $storeMock);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock, $storeMock)
+        );
     }
 
-    public function testAroundSaveNoNeed()
+    public function testBeforeAndAfterSaveNoNeed()
     {
         $this->mockConfigFlatEnabledNeever();
         $storeMock = $this->getMock(
@@ -108,7 +108,11 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->assertFalse($this->model->aroundSave($this->subjectMock, $this->closureMock, $storeMock));
+        $this->model->beforeSave($this->subjectMock, $storeMock);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock, $storeMock)
+        );
     }
 
     protected function mockIndexerMethods()
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreGroupTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreGroupTest.php
index 58385e8d38d7a4075de78fac4183cc112a70ff21..da2f90b313fbc48b5c5e3b51da5fb1d8381cd70c 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreGroupTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreGroupTest.php
@@ -5,39 +5,52 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Category\Product\Plugin;
 
-use \Magento\Catalog\Model\Indexer\Category\Product\Plugin\StoreGroup;
+use Magento\Catalog\Model\Indexer\Category\Product\Plugin\StoreGroup;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Store\Model\ResourceModel\Group;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Store\Model\Group as GroupModel;
+use Magento\Catalog\Model\Indexer\Category\Product;
 
 class StoreGroupTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var GroupModel|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerMock;
+    private $groupMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|
+     * @var \PHPUnit_Framework_MockObject_MockObject|IndexerInterface
      */
-    protected $pluginMock;
+    private $indexerMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \PHPUnit_Framework_MockObject_MockObject|Group
      */
-    protected $subject;
+    private $subject;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerRegistryMock;
+    private $indexerRegistryMock;
 
     /**
      * @var StoreGroup
      */
-    protected $model;
+    private $model;
 
     protected function setUp()
     {
+        $this->groupMock = $this->getMock(
+            GroupModel::class,
+            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
+            [],
+            '',
+            false
+        );
         $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
+            IndexerInterface::class,
             [],
             '',
             false,
@@ -45,57 +58,44 @@ class StoreGroupTest extends \PHPUnit_Framework_TestCase
             true,
             ['getId', 'getState', '__wakeup']
         );
-        $this->subject = $this->getMock(\Magento\Store\Model\ResourceModel\Group::class, [], [], '', false);
+        $this->subject = $this->getMock(Group::class, [], [], '', false);
         $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
+            IndexerRegistry::class,
             ['get'],
             [],
             '',
             false
         );
 
-        $this->model = new StoreGroup($this->indexerRegistryMock);
+        $this->model = (new ObjectManager($this))
+            ->getObject(StoreGroup::class, ['indexerRegistry' => $this->indexerRegistryMock]);
     }
 
     /**
      * @param array $valueMap
      * @dataProvider changedDataProvider
      */
-    public function testAroundSave($valueMap)
+    public function testBeforeAndAfterSave($valueMap)
     {
         $this->mockIndexerMethods();
-        $groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $groupMock->expects($this->exactly(2))->method('dataHasChangedFor')->will($this->returnValueMap($valueMap));
-        $groupMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(false));
+        $this->groupMock->expects($this->exactly(2))->method('dataHasChangedFor')->willReturnMap($valueMap);
+        $this->groupMock->expects($this->once())->method('isObjectNew')->willReturn(false);
 
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $groupMock));
+        $this->model->beforeSave($this->subject, $this->groupMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->groupMock));
     }
 
     /**
      * @param array $valueMap
      * @dataProvider changedDataProvider
      */
-    public function testAroundSaveNotNew($valueMap)
+    public function testBeforeAndAfterSaveNotNew($valueMap)
     {
-        $groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $groupMock->expects($this->exactly(2))->method('dataHasChangedFor')->will($this->returnValueMap($valueMap));
-        $groupMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(true));
+        $this->groupMock->expects($this->exactly(2))->method('dataHasChangedFor')->willReturnMap($valueMap);
+        $this->groupMock->expects($this->once())->method('isObjectNew')->willReturn(true);
 
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $groupMock));
+        $this->model->beforeSave($this->subject, $this->groupMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->groupMock));
     }
 
     public function changedDataProvider()
@@ -108,41 +108,23 @@ class StoreGroupTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
-    public function testAroundSaveWithoutChanges()
+    public function testBeforeAndAfterSaveWithoutChanges()
     {
-        $groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $groupMock->expects(
-            $this->exactly(2)
-        )->method(
-            'dataHasChangedFor'
-        )->will(
-            $this->returnValueMap([['root_category_id', false], ['website_id', false]])
-        );
-        $groupMock->expects($this->never())->method('isObjectNew');
+        $this->groupMock->expects($this->exactly(2))
+            ->method('dataHasChangedFor')
+            ->willReturnMap([['root_category_id', false], ['website_id', false]]);
+        $this->groupMock->expects($this->never())->method('isObjectNew');
 
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $groupMock));
+        $this->model->beforeSave($this->subject, $this->groupMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->groupMock));
     }
 
-    protected function mockIndexerMethods()
+    private function mockIndexerMethods()
     {
         $this->indexerMock->expects($this->once())->method('invalidate');
         $this->indexerRegistryMock->expects($this->once())
             ->method('get')
-            ->with(\Magento\Catalog\Model\Indexer\Category\Product::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
-    }
-
-    protected function mockPluginProceed($returnValue = false)
-    {
-        return function () use ($returnValue) {
-            return $returnValue;
-        };
+            ->with(Product::INDEXER_ID)
+            ->willReturn($this->indexerMock);
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreViewTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreViewTest.php
index 26dcfd206b124a5583915df373dcd258aa974500..f27d2b14f31cd3c7463b47ff8d01ad73e20dcfd5 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreViewTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/Product/Plugin/StoreViewTest.php
@@ -5,19 +5,23 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Category\Product\Plugin;
 
-use \Magento\Catalog\Model\Indexer\Category\Product\Plugin\StoreView;
+use Magento\Catalog\Model\Indexer\Category\Product\Plugin\StoreView;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Store\Model\ResourceModel\Group;
+use Magento\Store\Model\Store;
 
 class StoreViewTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var Store|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerMock;
+    private $storeMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|
+     * @var \PHPUnit_Framework_MockObject_MockObject|IndexerInterface
      */
-    protected $pluginMock;
+    protected $indexerMock;
 
     /**
      * @var StoreView
@@ -25,7 +29,7 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
     protected $model;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $indexerRegistryMock;
 
@@ -37,7 +41,7 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
     protected function setUp()
     {
         $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
+            IndexerInterface::class,
             [],
             '',
             false,
@@ -45,14 +49,21 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
             true,
             ['getId', 'getState', '__wakeup']
         );
-        $this->subject = $this->getMock(\Magento\Store\Model\ResourceModel\Group::class, [], [], '', false);
+        $this->subject = $this->getMock(Group::class, [], [], '', false);
         $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
+            IndexerRegistry::class,
             ['get'],
             [],
             '',
             false
         );
+        $this->storeMock = $this->getMock(
+            Store::class,
+            ['isObjectNew', 'dataHasChangedFor', '__wakeup'],
+            [],
+            '',
+            false
+        );
 
         $this->model = new StoreView($this->indexerRegistryMock);
     }
@@ -60,94 +71,38 @@ class StoreViewTest extends \PHPUnit_Framework_TestCase
     public function testAroundSaveNewObject()
     {
         $this->mockIndexerMethods();
-        $storeMock = $this->getMock(
-            \Magento\Store\Model\Store::class,
-            ['isObjectNew', 'dataHasChangedFor', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $storeMock->expects($this->once())->method('isObjectNew')->will($this->returnValue(true));
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $storeMock));
+        $this->storeMock->expects($this->once())->method('isObjectNew')->willReturn(true);
+        $this->model->beforeSave($this->subject, $this->storeMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->storeMock));
     }
 
     public function testAroundSaveHasChanged()
     {
         $this->mockIndexerMethods();
-        $storeMock = $this->getMock(
-            \Magento\Store\Model\Store::class,
-            ['isObjectNew', 'dataHasChangedFor', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $storeMock->expects(
-            $this->once()
-        )->method(
-            'dataHasChangedFor'
-        )->with(
-            'group_id'
-        )->will(
-            $this->returnValue(true)
-        );
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $storeMock));
+        $this->storeMock->expects($this->once())
+            ->method('dataHasChangedFor')
+            ->with('group_id')
+            ->willReturn(true);
+        $this->model->beforeSave($this->subject, $this->storeMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->storeMock));
     }
 
     public function testAroundSaveNoNeed()
     {
-        $storeMock = $this->getMock(
-            \Magento\Store\Model\Store::class,
-            ['isObjectNew', 'dataHasChangedFor', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $storeMock->expects(
-            $this->once()
-        )->method(
-            'dataHasChangedFor'
-        )->with(
-            'group_id'
-        )->will(
-            $this->returnValue(false)
-        );
-        $proceed = $this->mockPluginProceed();
-        $this->assertFalse($this->model->aroundSave($this->subject, $proceed, $storeMock));
-    }
-
-    /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Indexer\Model\Indexer\State
-     */
-    protected function getStateMock()
-    {
-        $stateMock = $this->getMock(
-            \Magento\Indexer\Model\Indexer\State::class,
-            ['setStatus', 'save', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $stateMock->expects($this->once())->method('setStatus')->with('invalid')->will($this->returnSelf());
-        $stateMock->expects($this->once())->method('save')->will($this->returnSelf());
-
-        return $stateMock;
+        $this->storeMock->expects($this->once())
+            ->method('dataHasChangedFor')
+            ->with('group_id')
+            ->willReturn(false);
+        $this->model->beforeSave($this->subject, $this->storeMock);
+        $this->assertSame($this->subject, $this->model->afterSave($this->subject, $this->subject, $this->storeMock));
     }
 
-    protected function mockIndexerMethods()
+    private function mockIndexerMethods()
     {
         $this->indexerMock->expects($this->once())->method('invalidate');
         $this->indexerRegistryMock->expects($this->once())
             ->method('get')
             ->with(\Magento\Catalog\Model\Indexer\Category\Product::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
-    }
-
-    protected function mockPluginProceed($returnValue = false)
-    {
-        return function () use ($returnValue) {
-            return $returnValue;
-        };
+            ->willReturn($this->indexerMock);
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Plugin/AttributeSetTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Plugin/AttributeSetTest.php
index 38f74d7c730c2d8d54b23ea71dd9c84dc9b3ee53..48410da746584d31d3bb5e543354fc1f8092019b 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Plugin/AttributeSetTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Plugin/AttributeSetTest.php
@@ -5,47 +5,105 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Plugin;
 
+use Magento\Catalog\Model\Indexer\Product\Eav\Plugin\AttributeSet\IndexableAttributeFilter;
+use Magento\Eav\Model\Entity\Attribute\Set as EavAttributeSet;
+use Magento\Eav\Model\Entity\Attribute\SetFactory;
+use Magento\Catalog\Model\Indexer\Product\Eav\Processor;
+use Magento\Catalog\Model\Indexer\Product\Eav\Plugin\AttributeSet;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
 class AttributeSetTest extends \PHPUnit_Framework_TestCase
 {
-    public function testAroundSave()
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var AttributeSet
+     */
+    private $model;
+
+    /**
+     * @var Processor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $eavProcessorMock;
+
+    /**
+     * @var IndexableAttributeFilter|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filterMock;
+
+    /**
+     * @var EavAttributeSet|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var SetFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $setFactoryMock;
+
+    /**
+     * @var EavAttributeSet|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $originalSetMock;
+
+    public function setUp()
     {
-        $eavProcessorMock = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Eav\Processor::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $eavProcessorMock->expects($this->once())
-            ->method('markIndexerAsInvalid');
-
-        $filter = $this->getMockBuilder(
-            \Magento\Catalog\Model\Indexer\Product\Eav\Plugin\AttributeSet\IndexableAttributeFilter::class
-        )
-            ->disableOriginalConstructor()
-            ->getMock();
-        $filter->expects($this->at(0))
-            ->method('filter')
-            ->will($this->returnValue([1, 2, 3]));
-        $filter->expects($this->at(1))
+        $this->filterMock = $this->getMock(IndexableAttributeFilter::class, [], [], '', false);
+        $this->subjectMock = $this->getMock(EavAttributeSet::class, [], [], '', false);
+        $this->eavProcessorMock = $this->getMock(Processor::class, [], [], '', false);
+        $this->setFactoryMock = $this->getMock(SetFactory::class, ['create'], [], '', false);
+        $this->objectManager = new ObjectManager($this);
+    }
+
+    public function testBeforeSave()
+    {
+        $setId = 1;
+        $this->originalSetMock = $this->getMock(EavAttributeSet::class, [], [], '', false);
+        $this->originalSetMock->expects($this->once())->method('initFromSkeleton')->with($setId);
+
+        $this->setFactoryMock->expects($this->once())->method('create')->willReturn($this->originalSetMock);
+        $this->model = $this->objectManager->getObject(
+            AttributeSet::class,
+            [
+                'indexerEavProcessor' => $this->eavProcessorMock,
+                'filter' => $this->filterMock,
+                'setFactory' => $this->setFactoryMock
+            ]
+        );
+
+        $this->filterMock->expects($this->exactly(2))
             ->method('filter')
-            ->will($this->returnValue([1, 2]));
+            ->willReturnMap(
+                [
+                    [$this->originalSetMock, [1, 2, 3]],
+                    [$this->subjectMock, [1, 2]]
+                ]
+            );
 
-        $subjectMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Set::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $subjectMock->expects($this->any())
+        $this->subjectMock->expects($this->exactly(2))
             ->method('getId')
-            ->will($this->returnValue(11));
+            ->willReturn($setId);
 
-        $model = new \Magento\Catalog\Model\Indexer\Product\Eav\Plugin\AttributeSet(
-            $eavProcessorMock,
-            $filter
-        );
+        $this->model->beforeSave($this->subjectMock);
+    }
 
-        $closure  = function () use ($subjectMock) {
-            return $subjectMock;
-        };
+    public function testAfterSave()
+    {
+        $this->eavProcessorMock->expects($this->once())->method('markIndexerAsInvalid');
 
-        $this->assertEquals(
-            $subjectMock,
-            $model->aroundSave($subjectMock, $closure)
-        );
+        $this->model = $this->objectManager
+            ->getObject(
+                AttributeSet::class,
+                [
+                    'indexerEavProcessor' => $this->eavProcessorMock,
+                    'filter' => $this->filterMock,
+                    'requiresReindex' => true
+                ]
+            );
+
+        $this->assertSame($this->subjectMock, $this->model->afterSave($this->subjectMock, $this->subjectMock));
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php
index 6624f2fa8260976ad2bec18aaff4ccde1f281e77..53d1a8fd004a080f6e3a9ac81d6962e7e72f6a73 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php
@@ -5,35 +5,47 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Flat\Plugin;
 
+use Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData as IndexerConfigDataPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Catalog\Model\Indexer\Product\Flat\State as ProductFlatIndexerState;
+use Magento\Indexer\Model\Config\Data as ConfigData;
+
 class IndexerConfigDataTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData
+     * @var IndexerConfigDataPlugin
      */
-    protected $model;
+    private $plugin;
 
     /**
-     * @var \Magento\Catalog\Model\Indexer\Product\Flat\State|\PHPUnit_Framework_MockObject_MockObject
+     * @var ObjectManagerHelper
      */
-    protected $_stateMock;
+    private $objectManagerHelper;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var ProductFlatIndexerState|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $subjectMock;
+    private $indexerStateMock;
+
+    /**
+     * @var ConfigData|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
 
     protected function setUp()
     {
-        $this->_stateMock = $this->getMock(
-            \Magento\Catalog\Model\Indexer\Product\Flat\State::class,
-            ['isFlatEnabled'],
-            [],
-            '',
-            false
-        );
-        $this->subjectMock = $this->getMock(\Magento\Indexer\Model\Config\Data::class, [], [], '', false);
+        $this->indexerStateMock = $this->getMockBuilder(ProductFlatIndexerState::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->subjectMock = $this->getMockBuilder(ConfigData::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $this->model = new \Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData($this->_stateMock);
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            IndexerConfigDataPlugin::class,
+            ['state' => $this->indexerStateMock]
+        );
     }
 
     /**
@@ -42,32 +54,36 @@ class IndexerConfigDataTest extends \PHPUnit_Framework_TestCase
      * @param mixed $default
      * @param array $inputData
      * @param array $outputData
-     * @dataProvider aroundGetDataProvider
+     *
+     * @dataProvider afterGetDataProvider
      */
-    public function testAroundGet($isFlat, $path, $default, $inputData, $outputData)
+    public function testAfterGet($isFlat, $path, $default, $inputData, $outputData)
     {
-        $closureMock = function () use ($inputData) {
-            return $inputData;
-        };
-        $this->_stateMock->expects($this->once())->method('isFlatEnabled')->will($this->returnValue($isFlat));
+        $this->indexerStateMock->expects(static::once())
+            ->method('isFlatEnabled')
+            ->willReturn($isFlat);
 
-        $this->assertEquals($outputData, $this->model->aroundGet($this->subjectMock, $closureMock, $path, $default));
+        $this->assertEquals($outputData, $this->plugin->afterGet($this->subjectMock, $inputData, $path, $default));
     }
 
-    public function aroundGetDataProvider()
+    /**
+     * @return array
+     */
+    public function afterGetDataProvider()
     {
         $flatIndexerData = [
             'indexer_id' => 'catalog_product_flat',
             'action' => '\Action\Class',
             'title' => 'Title',
-            'description' => 'Description',
+            'description' => 'Description'
         ];
         $otherIndexerData = [
             'indexer_id' => 'other_indexer',
             'action' => '\Action\Class',
             'title' => 'Title',
-            'description' => 'Description',
+            'description' => 'Description'
         ];
+
         return [
             // flat is enabled, nothing is being changed
             [
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Plugin/QuoteItemProductOptionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Plugin/QuoteItemProductOptionTest.php
index 6b6b0921e506aa2e81bee14e52c1d88ac6d221e6..13e9ef8b327874e97d9641aaeb568b9352ffc4c2 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Plugin/QuoteItemProductOptionTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Plugin/QuoteItemProductOptionTest.php
@@ -5,76 +5,95 @@
  */
 namespace Magento\Catalog\Test\Unit\Model\Plugin;
 
+use Magento\Catalog\Model\Plugin\QuoteItemProductOption as QuoteItemProductOptionPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Quote\Model\Quote\Item\ToOrderItem as QuoteToOrderItem;
+use Magento\Quote\Model\Quote\Item\AbstractItem as AbstractQuoteItem;
+use Magento\Quote\Model\Quote\Item\Option as QuoteItemOption;
+use Magento\Catalog\Model\Product;
+use Magento\Framework\DataObject;
+use Magento\Catalog\Model\Product\Option as ProductOption;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class QuoteItemProductOptionTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var \PHPUnit_Framework_MockObject_MockObject */
-    protected $quoteItemMock;
+    /**
+     * @var QuoteItemProductOptionPlugin
+     */
+    private $plugin;
 
-    /** @var \PHPUnit_Framework_MockObject_MockObject */
-    protected $orderItemMock;
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
 
-    /** @var \Magento\Catalog\Model\Plugin\QuoteItemProductOption */
-    protected $model;
+    /**
+     * @var QuoteToOrderItem|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
 
     /**
-     * @var \Closure
+     * @var AbstractQuoteItem|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $closureMock;
+    private $quoteItemMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var QuoteItemOption|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $subjectMock;
+    private $quoteItemOptionMock;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productMock;
 
     protected function setUp()
     {
-        $this->orderItemMock = $this->getMock(\Magento\Sales\Model\Order\Item::class, [], [], '', false);
-        $this->quoteItemMock = $this->getMock(\Magento\Quote\Model\Quote\Item::class, [], [], '', false);
-        $orderItem = $this->orderItemMock;
-        $this->subjectMock = $this->getMock(\Magento\Quote\Model\Quote\Item\ToOrderItem::class, [], [], '', false);
-        $this->closureMock = function () use ($orderItem) {
-            return $orderItem;
-        };
-        $this->model = new \Magento\Catalog\Model\Plugin\QuoteItemProductOption();
+        $this->subjectMock = $this->getMockBuilder(QuoteToOrderItem::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->quoteItemMock = $this->getMockBuilder(AbstractQuoteItem::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getOptions', 'getProduct'])
+            ->getMockForAbstractClass();
+        $this->quoteItemOptionMock = $this->getMockBuilder(QuoteItemOption::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCode'])
+            ->getMock();
+        $this->productMock = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(QuoteItemProductOptionPlugin::class);
     }
 
-    public function testAroundItemToOrderItemEmptyOptions()
+    public function testBeforeItemToOrderItemEmptyOptions()
     {
-        $this->quoteItemMock->expects($this->exactly(2))->method('getOptions')->will($this->returnValue([]));
+        $this->quoteItemMock->expects(static::once())
+            ->method('getOptions')
+            ->willReturn(null);
 
-        $orderItem = $this->model->aroundConvert($this->subjectMock, $this->closureMock, $this->quoteItemMock);
-        $this->assertSame($this->orderItemMock, $orderItem);
+        $this->plugin->beforeConvert($this->subjectMock, $this->quoteItemMock);
     }
 
-    public function testAroundItemToOrderItemWithOptions()
+    public function testBeforeItemToOrderItemWithOptions()
     {
-        $itemOption = $this->getMock(
-            \Magento\Quote\Model\Quote\Item\Option::class,
-            ['getCode', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $this->quoteItemMock->expects(
-            $this->exactly(2)
-        )->method(
-            'getOptions'
-        )->will(
-            $this->returnValue([$itemOption, $itemOption])
-        );
-
-        $itemOption->expects($this->at(0))->method('getCode')->will($this->returnValue('someText_8'));
-        $itemOption->expects($this->at(1))->method('getCode')->will($this->returnValue('not_int_text'));
-
-        $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false);
-        $optionMock = $this->getMock(\stdClass::class, ['getType']);
-        $optionMock->expects($this->once())->method('getType');
-
-        $productMock->expects($this->once())->method('getOptionById')->will($this->returnValue($optionMock));
-
-        $this->quoteItemMock->expects($this->once())->method('getProduct')->will($this->returnValue($productMock));
+        $this->quoteItemMock->expects(static::exactly(2))
+            ->method('getOptions')
+            ->willReturn([$this->quoteItemOptionMock, $this->quoteItemOptionMock]);
+        $this->quoteItemOptionMock->expects(static::exactly(2))
+            ->method('getCode')
+            ->willReturnOnConsecutiveCalls('someText_8', 'not_int_text');
+        $this->productMock->expects(static::once())
+            ->method('getOptionById')
+            ->willReturn(new DataObject(['type' => ProductOption::OPTION_TYPE_FILE]));
+        $this->quoteItemMock->expects(static::once())
+            ->method('getProduct')
+            ->willReturn($this->productMock);
 
-        $orderItem = $this->model->aroundConvert($this->subjectMock, $this->closureMock, $this->quoteItemMock);
-        $this->assertSame($this->orderItemMock, $orderItem);
+        $this->plugin->beforeConvert($this->subjectMock, $this->quoteItemMock);
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/Attribute/SaveTest.php
index cb01f50605f664e01d1aee21c735054ea471e9a4..58e88952de2266f434a1ad0bbd1a99617edf1e8b 100644
--- a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/Attribute/SaveTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/ResourceModel/Attribute/SaveTest.php
@@ -6,46 +6,51 @@
 
 namespace Magento\Catalog\Test\Unit\Plugin\Model\ResourceModel\Attribute;
 
-use \Magento\Catalog\Plugin\Model\ResourceModel\Attribute\Save;
+use Magento\Catalog\Plugin\Model\ResourceModel\Attribute\Save;
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\Cache\TypeListInterface;
+use Magento\Catalog\Model\ResourceModel\Attribute;
 
 class SaveTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var \Magento\Catalog\Plugin\Model\ResourceModel\Attribute\Save */
+    /**
+     * @var Attribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Save
+     */
     protected $save;
 
-    /** @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject */
+    /**
+     * @var Config|\PHPUnit_Framework_MockObject_MockObject
+     */
     protected $config;
 
-    /** @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject */
+    /**
+     * @var TypeListInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
     protected $typeList;
 
     protected function setUp()
     {
-        $this->config = $this->getMockBuilder(\Magento\PageCache\Model\Config::class)
-            ->disableOriginalConstructor()
-            ->setMethods(['isEnabled'])
-            ->getMock();
-        $this->typeList = $this->getMockBuilder(\Magento\Framework\App\Cache\TypeListInterface::class)
-            ->disableOriginalConstructor()
-            ->setMethods(['invalidate'])
-            ->getMockForAbstractClass();
-
+        $this->config = $this->getMock(Config::class, ['isEnabled'], [], '', false);
+        $this->typeList = $this->getMockForAbstractClass(
+            TypeListInterface::class,
+            [],
+            '',
+            false,
+            false,
+            true,
+            ['invalidate']
+        );
+        $this->subjectMock = $this->getMock(Attribute::class, [], [], '', false);
         $this->save = new Save($this->config, $this->typeList);
     }
 
-    public function testAroundSaveWithoutInvalidate()
+    public function testAfterSaveWithoutInvalidate()
     {
-        $subject = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Attribute::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $self = $this;
-        $proceed = function ($object) use ($self, $attribute) {
-            $self->assertEquals($object, $attribute);
-        };
-
         $this->config->expects($this->once())
             ->method('isEnabled')
             ->willReturn(false);
@@ -53,23 +58,11 @@ class SaveTest extends \PHPUnit_Framework_TestCase
         $this->typeList->expects($this->never())
             ->method('invalidate');
 
-        $this->save->aroundSave($subject, $proceed, $attribute);
+        $this->assertSame($this->subjectMock, $this->save->afterSave($this->subjectMock, $this->subjectMock));
     }
 
-    public function testAroundSave()
+    public function testAfterSave()
     {
-        $subject = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Attribute::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-        $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $self = $this;
-        $proceed = function ($object) use ($self, $attribute) {
-            $self->assertEquals($object, $attribute);
-        };
-
         $this->config->expects($this->once())
             ->method('isEnabled')
             ->willReturn(true);
@@ -78,6 +71,6 @@ class SaveTest extends \PHPUnit_Framework_TestCase
             ->method('invalidate')
             ->with('full_page');
 
-        $this->save->aroundSave($subject, $proceed, $attribute);
+        $this->assertSame($this->subjectMock, $this->save->afterSave($this->subjectMock, $this->subjectMock));
     }
 }
diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php
index df910254082185145212c325568a971a75a9bd85..14b7207a1e43bafe167f8a8e73ec2ae3281b87ba 100644
--- a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php
+++ b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php
@@ -14,7 +14,13 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface;
 use Magento\CatalogInventory\Api\StockRegistryInterface;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\Exception\CouldNotSaveException;
 
+/**
+ * Plugin for Magento\Catalog\Api\ProductRepositoryInterface
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class AroundProductRepositorySave
 {
     /**
@@ -53,26 +59,22 @@ class AroundProductRepositorySave
      *
      * Pay attention that in this code we mostly work with original product object to process stock item data,
      * not with received result (saved product) because it is already contains new empty stock item object.
-     * It is a reason why this plugin cannot be rewritten to after plugin
      *
-     * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
-     * @param callable $proceed
-     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param ProductRepositoryInterface $subject
+     * @param ProductInterface $result
+     * @param ProductInterface $product
      * @param bool $saveOptions
-     * @return \Magento\Catalog\Api\Data\ProductInterface
-     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @return ProductInterface
+     * @throws CouldNotSaveException
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Catalog\Api\ProductRepositoryInterface $subject,
-        \Closure $proceed,
-        \Magento\Catalog\Api\Data\ProductInterface $product,
+    public function afterSave(
+        ProductRepositoryInterface $subject,
+        ProductInterface $result,
+        ProductInterface $product,
         $saveOptions = false
     ) {
-        /**
-         * @var \Magento\Catalog\Api\Data\ProductInterface $result
-         */
-        $result = $proceed($product, $saveOptions);
-
         /* @var StockItemInterface $stockItem */
         $stockItem = $this->getStockItemToBeUpdated($product);
         if (null === $stockItem) {
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
index d4146182e205f353a9368bf07f0da07ce7ff7985..2d997c92f266687953ce72833f0924f06ae6fdd4 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
@@ -6,7 +6,7 @@
 
 namespace Magento\CatalogInventory\Test\Unit\Model\Plugin;
 
-use Magento\Catalog\Api\Data\ProductExtension;
+use Magento\Catalog\Api\Data\ProductExtensionInterface;
 use Magento\Catalog\Api\Data\ProductInterface;
 use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\CatalogInventory\Api\Data\StockInterface;
@@ -23,22 +23,17 @@ use Magento\Store\Model\StoreManagerInterface;
 class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Closure
-     */
-    private $closure;
-
-    /**
-     * @var ProductInterface
+     * @var ProductInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     private $product;
 
     /**
-     * @var ProductInterface
+     * @var ProductInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     private $savedProduct;
 
     /**
-     * @var ProductExtension|\PHPUnit_Framework_MockObject_MockObject
+     * @var ProductExtensionInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     private $productExtension;
 
@@ -73,7 +68,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
     private $stockConfiguration;
 
     /**
-     * @var \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave
+     * @var AroundProductRepositorySave
      */
     private $plugin;
 
@@ -97,23 +92,25 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             $this->stockConfiguration
         );
 
-        $this->savedProduct = $savedProduct = $this->getMockBuilder(ProductInterface::class)
+        $this->savedProduct = $this->getMockBuilder(ProductInterface::class)
             ->setMethods(['getExtensionAttributes', 'getStoreId'])
             ->getMockForAbstractClass();
 
-        $this->closure = function () use ($savedProduct) {
-            return $savedProduct;
-        };
-
         $this->productRepository = $this->getMockBuilder(ProductRepositoryInterface::class)
             ->setMethods(['get'])
             ->getMockForAbstractClass();
         $this->product = $this->getMockBuilder(ProductInterface::class)
             ->setMethods(['getExtensionAttributes', 'getStoreId'])
             ->getMockForAbstractClass();
-        $this->productExtension = $this->getMockBuilder(ProductExtension::class)
-            ->setMethods(['getStockItem'])
-            ->getMock();
+        $this->productExtension = $this->getMockForAbstractClass(
+            ProductExtensionInterface::class,
+            [],
+            '',
+            false,
+            false,
+            true,
+            ['getStockItem']
+        );
         $this->stockItem = $this->getMockBuilder(StockItemInterface::class)
             ->setMethods(['setWebsiteId', 'getWebsiteId', 'getStockId'])
             ->getMockForAbstractClass();
@@ -122,7 +119,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->getMockForAbstractClass();
     }
 
-    public function testAroundSaveWhenProductHasNoStockItemNeedingToBeUpdated()
+    public function testAfterSaveWhenProductHasNoStockItemNeedingToBeUpdated()
     {
         // pretend we have no extension attributes at all
         $this->product->expects($this->once())
@@ -137,14 +134,14 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
         $this->stockItem->expects($this->never())->method('setWebsiteId');
 
         // expect that there are no changes to the existing stock item information
-        $result = $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $result = $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product);
         $this->assertEquals(
             $this->savedProduct,
             $result
         );
     }
 
-    public function testAroundSaveWhenProductHasNoPersistentStockItemInfo()
+    public function testAfterSaveWhenProductHasNoPersistentStockItemInfo()
     {
         // pretend we do have extension attributes, but none for 'stock_item'
         $this->product->expects($this->once())
@@ -169,11 +166,11 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $newProductMock,
-            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
+            $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product)
         );
     }
 
-    public function testAroundSave()
+    public function testAfterSave()
     {
         $productId = 5494;
         $storeId = 2;
@@ -224,7 +221,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $newProductMock,
-            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
+            $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product)
         );
     }
 
@@ -232,7 +229,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
      * @expectedException \Magento\Framework\Exception\LocalizedException
      * @expectedExceptionMessage Invalid stock id: 100500. Only default stock with id 50 allowed
      */
-    public function testAroundSaveWithInvalidStockId()
+    public function testAfterSaveWithInvalidStockId()
     {
         $stockId = 100500;
         $defaultScopeId = 100;
@@ -259,14 +256,14 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->method('getStockItem')
             ->willReturn($this->stockItem);
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\LocalizedException
      * @expectedExceptionMessage Invalid stock item id: 0. Should be null or numeric value greater than 0
      */
-    public function testAroundSaveWithInvalidStockItemId()
+    public function testAfterSaveWithInvalidStockItemId()
     {
         $stockId = 80;
         $stockItemId = 0;
@@ -298,14 +295,14 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->method('getItemId')
             ->willReturn($stockItemId);
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\LocalizedException
      * @expectedExceptionMessage Invalid stock item id: 35. Assigned stock item id is 40
      */
-    public function testAroundSaveWithNotAssignedStockItemId()
+    public function testAfterSaveWithNotAssignedStockItemId()
     {
         $stockId = 80;
         $stockItemId = 35;
@@ -348,6 +345,6 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->method('getStockItem')
             ->willReturn($storedStockItem);
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product);
     }
 }
diff --git a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRules.php b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRules.php
index 2f007a83a5fe1bec524a2c4b9db5f5ce39fb15c2..00cdfb5aaaf79e22d350d95bbb523a097c1c5fe4 100644
--- a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRules.php
+++ b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRules.php
@@ -26,18 +26,16 @@ class ApplyRules
      * Apply catalog rules after product resource model save
      *
      * @param \Magento\Catalog\Model\ResourceModel\Product $subject
-     * @param callable $proceed
+     * @param \Magento\Catalog\Model\ResourceModel\Product $productResource
      * @param \Magento\Framework\Model\AbstractModel $product
      * @return \Magento\Catalog\Model\ResourceModel\Product
-     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
+    public function afterSave(
         \Magento\Catalog\Model\ResourceModel\Product $subject,
-        callable $proceed,
+        \Magento\Catalog\Model\ResourceModel\Product $productResource,
         \Magento\Framework\Model\AbstractModel $product
     ) {
-        $productResource = $proceed($product);
         if (!$product->getIsMassupdate()) {
             $this->productRuleProcessor->reindexRow($product->getId());
         }
diff --git a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRulesAfterReindex.php b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRulesAfterReindex.php
index af00ef3aada024fe5f16e275247a6c7b18d0522f..5416c2e59b00cae1f4f9d94475f955ba4561593b 100644
--- a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRulesAfterReindex.php
+++ b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Save/ApplyRulesAfterReindex.php
@@ -6,7 +6,11 @@
 namespace Magento\CatalogRule\Plugin\Indexer\Product\Save;
 
 use Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor;
+use Magento\Catalog\Model\Product;
 
+/**
+ * Plugin for Magento\Catalog\Model\Product
+ */
 class ApplyRulesAfterReindex
 {
     /**
@@ -25,16 +29,11 @@ class ApplyRulesAfterReindex
     /**
      * Apply catalog rules after product resource model save
      *
-     * @param \Magento\Catalog\Model\Product $subject
-     * @param callable $proceed
-     * @return \Magento\Catalog\Model\Product
+     * @param Product $subject
+     * @return void
      */
-    public function aroundReindex(
-        \Magento\Catalog\Model\Product $subject,
-        callable $proceed
-    ) {
-        $proceed();
+    public function afterReindex(Product $subject)
+    {
         $this->productRuleProcessor->reindexRow($subject->getId());
-        return;
     }
 }
diff --git a/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesAfterReindexTest.php b/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesAfterReindexTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d2068c9f52651d95682a2b493d4481fb5349706
--- /dev/null
+++ b/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesAfterReindexTest.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogRule\Test\Unit\Plugin\Indexer\Product\Save;
+
+use Magento\CatalogRule\Plugin\Indexer\Product\Save\ApplyRulesAfterReindex;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor;
+use Magento\Catalog\Model\Product;
+
+class ApplyRulesAfterReindexTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ApplyRulesAfterReindex
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var ProductRuleProcessor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productRuleProcessorMock;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    protected function setUp()
+    {
+        $this->productRuleProcessorMock = $this->getMockBuilder(ProductRuleProcessor::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->subjectMock = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            ApplyRulesAfterReindex::class,
+            ['productRuleProcessor' => $this->productRuleProcessorMock]
+        );
+    }
+
+    public function testAfterReindex()
+    {
+        $id = 'test_id';
+
+        $this->subjectMock->expects(static::any())
+            ->method('getId')
+            ->willReturn($id);
+        $this->productRuleProcessorMock->expects(static::once())
+            ->method('reindexRow')
+            ->with($id, false);
+
+        $this->plugin->afterReindex($this->subjectMock);
+    }
+}
diff --git a/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesTest.php b/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..59031959b51adce5ef0320dbbeac5aa174d15eb1
--- /dev/null
+++ b/app/code/Magento/CatalogRule/Test/Unit/Plugin/Indexer/Product/Save/ApplyRulesTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogRule\Test\Unit\Plugin\Indexer\Product\Save;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class ApplyRulesTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productRuleProcessor;
+
+    /**
+     * @var \Magento\Catalog\Model\ResourceModel\Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subject;
+
+    /**
+     * @var \Magento\Framework\Model\AbstractModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $model;
+
+    /**
+     * @var \Magento\CatalogRule\Plugin\Indexer\Product\Save\ApplyRules
+     */
+    private $plugin;
+
+    protected function setUp()
+    {
+        $this->productRuleProcessor = $this
+            ->getMockBuilder(\Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->subject = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->model = $this->getMockForAbstractClass(
+            \Magento\Framework\Model\AbstractModel::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getIsMassupdate', 'getId']
+        );
+
+        $this->plugin = (new ObjectManager($this))->getObject(
+            \Magento\CatalogRule\Plugin\Indexer\Product\Save\ApplyRules::class,
+            [
+                'productRuleProcessor' => $this->productRuleProcessor,
+            ]
+        );
+    }
+
+    public function testAfterSave()
+    {
+        $this->model->expects($this->once())->method('getIsMassupdate')->willReturn(null);
+        $this->model->expects($this->once())->method('getId')->willReturn(1);
+
+        $this->productRuleProcessor->expects($this->once())->method('reindexRow')->willReturnSelf();
+
+        $this->assertSame(
+            $this->subject,
+            $this->plugin->afterSave($this->subject, $this->subject, $this->model)
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/Validation.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/Validation.php
index 7164de93bc705f06647f1ef847bbc0a81cb8a7a3..607f02ba7902c921a8d3ad2f12d7d5798cd6024f 100644
--- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/Validation.php
+++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/Validation.php
@@ -6,18 +6,23 @@
  */
 namespace Magento\CatalogRuleConfigurable\Plugin\CatalogRule\Model\Rule;
 
-use \Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\CatalogRule\Model\Rule;
+use Magento\Framework\DataObject;
+use Magento\Catalog\Model\Product;
 
 /**
  * Class Validation. Call validate method for configurable product instead simple product
  */
 class Validation
 {
-    /** @var Configurable */
+    /**
+     * @var Configurable
+     */
     private $configurable;
 
     /**
-     * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableType
+     * @param Configurable $configurableType
      */
     public function __construct(Configurable $configurableType)
     {
@@ -25,17 +30,15 @@ class Validation
     }
 
     /**
-     * @param \Magento\CatalogRule\Model\Rule $rule
-     * @param \Closure $proceed
-     * @param \Magento\Framework\DataObject|\Magento\Catalog\Model\Product $product
+     * Define if it is needed to apply rule if parent configurable product match conditions
+     *
+     * @param Rule $rule
+     * @param bool $validateResult
+     * @param DataObject|Product $product
      * @return bool
      */
-    public function aroundValidate(
-        \Magento\CatalogRule\Model\Rule $rule,
-        \Closure $proceed,
-        \Magento\Framework\DataObject $product
-    ) {
-        $validateResult = $proceed($product);
+    public function afterValidate(Rule $rule, $validateResult, DataObject $product)
+    {
         if (!$validateResult && ($configurableProducts = $this->configurable->getParentIdsByChild($product->getId()))) {
             foreach ($configurableProducts as $configurableProductId) {
                 $validateResult = $rule->getConditions()->validateByEntityId($configurableProductId);
diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Unit/Plugin/CatalogRule/Model/Rule/ValidationTest.php b/app/code/Magento/CatalogRuleConfigurable/Test/Unit/Plugin/CatalogRule/Model/Rule/ValidationTest.php
index b7c95fe97ac2082d2a5e48b5722c4f9e2516688d..6ed0cb1d48979ad7a715c09eedd0556f0e742e15 100644
--- a/app/code/Magento/CatalogRuleConfigurable/Test/Unit/Plugin/CatalogRule/Model/Rule/ValidationTest.php
+++ b/app/code/Magento/CatalogRuleConfigurable/Test/Unit/Plugin/CatalogRule/Model/Rule/ValidationTest.php
@@ -62,16 +62,12 @@ class ValidationTest extends \PHPUnit_Framework_TestCase
      * @dataProvider dataProviderForValidateWithValidConfigurableProduct
      * @return void
      */
-    public function testAroundValidateWithValidConfigurableProduct(
+    public function testAfterValidateWithValidConfigurableProduct(
         $parentsIds,
         $validationResult,
         $runValidateAmount,
         $result
     ) {
-        $closureMock = function () {
-            return false;
-        };
-
         $this->productMock->expects($this->once())->method('getId')->willReturn('product_id');
         $this->configurableMock->expects($this->once())->method('getParentIdsByChild')->with('product_id')
             ->willReturn($parentsIds);
@@ -82,7 +78,7 @@ class ValidationTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $result,
-            $this->validation->aroundValidate($this->ruleMock, $closureMock, $this->productMock)
+            $this->validation->afterValidate($this->ruleMock, false, $this->productMock)
         );
     }
 
diff --git a/app/code/Magento/CatalogRuleConfigurable/composer.json b/app/code/Magento/CatalogRuleConfigurable/composer.json
index 06c3019cfb12fefeebfca78acd1b9553628df57d..cc51269e2d9720e380612cf56c712ff2459bbe35 100644
--- a/app/code/Magento/CatalogRuleConfigurable/composer.json
+++ b/app/code/Magento/CatalogRuleConfigurable/composer.json
@@ -5,6 +5,7 @@
         "php": "~5.6.0|7.0.2|7.0.4|~7.0.6",
         "magento/module-configurable-product": "100.2.*",
         "magento/framework": "100.2.*",
+        "magento/module-catalog": "101.1.*",
         "magento/module-catalog-rule": "100.2.*",
         "magento/module-store": "100.2.*",
         "magento/module-customer": "100.2.*",
diff --git a/app/code/Magento/CatalogSearch/Block/Plugin/FrontTabPlugin.php b/app/code/Magento/CatalogSearch/Block/Plugin/FrontTabPlugin.php
index de344754d16fe8dd79d0bb4aca61cb1e7070aa76..9b5c324d0dfa01f784d34307104fc9141128f301 100644
--- a/app/code/Magento/CatalogSearch/Block/Plugin/FrontTabPlugin.php
+++ b/app/code/Magento/CatalogSearch/Block/Plugin/FrontTabPlugin.php
@@ -5,11 +5,14 @@
  */
 namespace Magento\CatalogSearch\Block\Plugin;
 
-use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Front;
+use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Front as ProductAttributeFrontTabBlock;
 use Magento\CatalogSearch\Model\Source\Weight;
 use Magento\Framework\Data\Form;
 use Magento\Framework\Data\Form\Element\Fieldset;
 
+/**
+ * Plugin for Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Front
+ */
 class FrontTabPlugin
 {
     /**
@@ -26,15 +29,14 @@ class FrontTabPlugin
     }
 
     /**
-     * @param Front $subject
-     * @param callable $proceed
+     * Add Search Weight field
+     *
+     * @param ProductAttributeFrontTabBlock $subject
      * @param Form $form
-     * @return Front
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @return void
      */
-    public function aroundSetForm(Front $subject, \Closure $proceed, Form $form)
+    public function beforeSetForm(ProductAttributeFrontTabBlock $subject, Form $form)
     {
-        $block = $proceed($form);
         /** @var Fieldset $fieldset */
         $fieldset = $form->getElement('front_fieldset');
         $fieldset->addField(
@@ -47,17 +49,8 @@ class FrontTabPlugin
             ],
             'is_searchable'
         );
-
         $subject->getChildBlock('form_after')
-            ->addFieldMap(
-                'search_weight',
-                'search_weight'
-            )
-            ->addFieldDependence(
-                'search_weight',
-                'searchable',
-                '1'
-            );
-        return $block;
+            ->addFieldMap('search_weight', 'search_weight')
+            ->addFieldDependence('search_weight', 'searchable', '1');
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AbstractPlugin.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AbstractPlugin.php
index bbdb24bec0918b8bbc4eef14b60867bfcceff5a2..956f07206c20f03f6930c56b62a25ec8f3fceda0 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AbstractPlugin.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/AbstractPlugin.php
@@ -5,19 +5,24 @@
  */
 namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin;
 
-use Magento\CatalogSearch\Model\Indexer\Fulltext;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
+/**
+ * Abstract plugin for indexers
+ */
 abstract class AbstractPlugin
 {
-    /** @var \Magento\Framework\Indexer\IndexerRegistry */
+    /**
+     * @var IndexerRegistry
+     */
     protected $indexerRegistry;
 
     /**
-     * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
+     * @param IndexerRegistry $indexerRegistry
      */
-    public function __construct(
-        \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
-    ) {
+    public function __construct(IndexerRegistry $indexerRegistry)
+    {
         $this->indexerRegistry = $indexerRegistry;
     }
 
@@ -29,7 +34,8 @@ abstract class AbstractPlugin
      */
     protected function reindexRow($productId)
     {
-        $indexer = $this->indexerRegistry->get(Fulltext::INDEXER_ID);
+        $indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID);
+
         if (!$indexer->isScheduled()) {
             $indexer->reindexRow($productId);
         }
@@ -43,7 +49,8 @@ abstract class AbstractPlugin
      */
     protected function reindexList(array $productIds)
     {
-        $indexer = $this->indexerRegistry->get(Fulltext::INDEXER_ID);
+        $indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID);
+
         if (!$indexer->isScheduled()) {
             $indexer->reindexList($productIds);
         }
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php
index e358fe2ca5a28d8a9a098c6b771799a9335083e4..e21f4a976af8ecd6aa920e71dd50755e614ece61 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php
@@ -14,6 +14,21 @@ class Attribute extends AbstractPlugin
      */
     private $config;
 
+    /**
+     * @var boolean
+     */
+    private $deleteNeedInvalidation;
+
+    /**
+     * @var boolean
+     */
+    private $saveNeedInvalidation;
+
+    /**
+     * @var boolean
+     */
+    private $saveIsNew;
+
     /**
      * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
      * @param \Magento\Framework\Search\Request\Config $config
@@ -27,32 +42,43 @@ class Attribute extends AbstractPlugin
     }
 
     /**
-     * Invalidate indexer on attribute save (searchable flag change)
+     * Check if indexer invalidation is needed on attribute save (searchable flag change)
      *
      * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
-     * @param \Closure $proceed
      * @param \Magento\Framework\Model\AbstractModel $attribute
      *
-     * @return \Magento\Catalog\Model\ResourceModel\Attribute
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
+    public function beforeSave(
         \Magento\Catalog\Model\ResourceModel\Attribute $subject,
-        \Closure $proceed,
         \Magento\Framework\Model\AbstractModel $attribute
     ) {
-        $isNew = $attribute->isObjectNew();
-        $needInvalidation = (
+        $this->saveIsNew = $attribute->isObjectNew();
+        $this->saveNeedInvalidation = (
                 $attribute->dataHasChangedFor('is_searchable')
                 || $attribute->dataHasChangedFor('is_filterable')
                 || $attribute->dataHasChangedFor('is_visible_in_advanced_search')
-            ) && !$isNew;
+            ) && ! $this->saveIsNew;
+    }
 
-        $result = $proceed($attribute);
-        if ($needInvalidation) {
+    /**
+     * Invalidate indexer on attribute save (searchable flag change)
+     *
+     * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
+     * @param \Magento\Catalog\Model\ResourceModel\Attribute $result
+     *
+     * @return \Magento\Catalog\Model\ResourceModel\Attribute
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterSave(
+        \Magento\Catalog\Model\ResourceModel\Attribute $subject,
+        \Magento\Catalog\Model\ResourceModel\Attribute $result
+    ) {
+        if ($this->saveNeedInvalidation) {
             $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
         }
-        if ($isNew || $needInvalidation) {
+        if ($this->saveIsNew || $this->saveNeedInvalidation) {
             $this->config->reset();
         }
 
@@ -60,26 +86,37 @@ class Attribute extends AbstractPlugin
     }
 
     /**
-     * Invalidate indexer on searchable attribute delete
+     * Check if indexer invalidation is needed on searchable attribute delete
      *
      * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
-     * @param \Closure $proceed
      * @param \Magento\Framework\Model\AbstractModel $attribute
      *
-     * @return \Magento\Catalog\Model\ResourceModel\Attribute
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDelete(
+    public function beforeDelete(
         \Magento\Catalog\Model\ResourceModel\Attribute $subject,
-        \Closure $proceed,
         \Magento\Framework\Model\AbstractModel $attribute
     ) {
-        $needInvalidation = !$attribute->isObjectNew() && $attribute->getIsSearchable();
-        $result = $proceed($attribute);
-        if ($needInvalidation) {
+        $this->deleteNeedInvalidation = !$attribute->isObjectNew() && $attribute->getIsSearchable();
+    }
+
+    /**
+     * Invalidate indexer on searchable attribute delete
+     *
+     * @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
+     * @param \Magento\Catalog\Model\ResourceModel\Attribute $result
+     *
+     * @return \Magento\Catalog\Model\ResourceModel\Attribute
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterDelete(
+        \Magento\Catalog\Model\ResourceModel\Attribute $subject,
+        \Magento\Catalog\Model\ResourceModel\Attribute $result
+    ) {
+        if ($this->deleteNeedInvalidation) {
             $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
         }
-
         return $result;
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Action.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Action.php
index 5c43eb673591adf00e9e9eb6bd2e491cd5f585af..c144737606a3809cba52448a7485f3000aa51343 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Action.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Action.php
@@ -3,56 +3,54 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product;
 
-use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin as AbstractIndexerPlugin;
+use Magento\Catalog\Model\Product\Action as ProductAction;
 
-class Action extends AbstractPlugin
+/**
+ * Plugin for Magento\Catalog\Model\Product\Action
+ */
+class Action extends AbstractIndexerPlugin
 {
     /**
      * Reindex on product attribute mass change
      *
-     * @param \Magento\Catalog\Model\Product\Action $subject
-     * @param \Closure $closure
+     * @param ProductAction $subject
+     * @param ProductAction $action
      * @param array $productIds
      * @param array $attrData
      * @param int $storeId
-     * @return \Magento\Catalog\Model\Product\Action
+     * @return ProductAction
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundUpdateAttributes(
-        \Magento\Catalog\Model\Product\Action $subject,
-        \Closure $closure,
-        array $productIds,
-        array $attrData,
+    public function afterUpdateAttributes(
+        ProductAction $subject,
+        ProductAction $action,
+        $productIds,
+        $attrData,
         $storeId
     ) {
-        $result = $closure($productIds, $attrData, $storeId);
         $this->reindexList(array_unique($productIds));
-        return $result;
+
+        return $action;
     }
 
     /**
      * Reindex on product websites mass change
      *
-     * @param \Magento\Catalog\Model\Product\Action $subject
-     * @param \Closure $closure
+     * @param ProductAction $subject
+     * @param null $result
      * @param array $productIds
      * @param array $websiteIds
      * @param string $type
-     * @return \Magento\Catalog\Model\Product\Action
+     * @return void
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundUpdateWebsites(
-        \Magento\Catalog\Model\Product\Action $subject,
-        \Closure $closure,
-        array $productIds,
-        array $websiteIds,
-        $type
-    ) {
-        $result = $closure($productIds, $websiteIds, $type);
+    public function afterUpdateWebsites(ProductAction $subject, $result, $productIds, $websiteIds, $type)
+    {
         $this->reindexList(array_unique($productIds));
-        return $result;
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/Group.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/Group.php
index 0836760a2f5897f991914ae6d27f51566a5207e1..ed431110e04902f962f662d6b82d85e3a9f0426c 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/Group.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/Group.php
@@ -5,30 +5,48 @@
  */
 namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store;
 
-use Magento\CatalogSearch\Model\Indexer\Fulltext;
-use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin as AbstractIndexerPlugin;
+use Magento\Store\Model\ResourceModel\Group as StoreGroupResourceModel;
+use Magento\Framework\Model\AbstractModel;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
-class Group extends AbstractPlugin
+/**
+ * Plugin for Magento\Store\Model\ResourceModel\Group
+ */
+class Group extends AbstractIndexerPlugin
 {
+    /**
+     * @var bool
+     */
+    private $needInvalidation;
+
+    /**
+     * Check if indexer requires invalidation after store group save
+     *
+     * @param StoreGroupResourceModel $subject
+     * @param AbstractModel $group
+     * @return void
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function beforeSave(StoreGroupResourceModel $subject, AbstractModel $group)
+    {
+        $this->needInvalidation = !$group->isObjectNew() && $group->dataHasChangedFor('website_id');
+    }
+
     /**
      * Invalidate indexer on store group save
      *
-     * @param \Magento\Store\Model\ResourceModel\Group $subject
-     * @param \Closure $proceed
-     * @param \Magento\Framework\Model\AbstractModel $group
+     * @param StoreGroupResourceModel $subject
+     * @param StoreGroupResourceModel $result
+     * @return StoreGroupResourceModel
      *
-     * @return \Magento\Store\Model\ResourceModel\Group
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Store\Model\ResourceModel\Group $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $group
-    ) {
-        $needInvalidation = !$group->isObjectNew() && $group->dataHasChangedFor('website_id');
-        $result = $proceed($group);
-        if ($needInvalidation) {
-            $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
+    public function afterSave(StoreGroupResourceModel $subject, StoreGroupResourceModel $result)
+    {
+        if ($this->needInvalidation) {
+            $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID)->invalidate();
         }
 
         return $result;
@@ -37,17 +55,16 @@ class Group extends AbstractPlugin
     /**
      * Invalidate indexer on store group delete
      *
-     * @param \Magento\Store\Model\ResourceModel\Group $subject
-     * @param \Magento\Store\Model\ResourceModel\Group $result
+     * @param StoreGroupResourceModel $subject
+     * @param StoreGroupResourceModel $result
+     * @return StoreGroupResourceModel
      *
-     * @return \Magento\Store\Model\ResourceModel\Group
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function afterDelete(
-        \Magento\Store\Model\ResourceModel\Group $subject,
-        \Magento\Store\Model\ResourceModel\Group $result
-    ) {
-        $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
+    public function afterDelete(StoreGroupResourceModel $subject, StoreGroupResourceModel $result)
+    {
+        $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID)->invalidate();
+
         return $result;
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/View.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/View.php
index 128a1c07e93ffa179dc96be8cf7624b34ce6a446..5d57b71634d56153b94dd37f566f70323a4565de 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/View.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Store/View.php
@@ -5,48 +5,66 @@
  */
 namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store;
 
-use Magento\CatalogSearch\Model\Indexer\Fulltext;
-use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\AbstractPlugin as AbstractIndexerPlugin;
+use Magento\Store\Model\ResourceModel\Store as StoreResourceModel;
+use Magento\Framework\Model\AbstractModel;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
-class View extends AbstractPlugin
+/**
+ * Plugin for Magento\Store\Model\ResourceModel\Store
+ */
+class View extends AbstractIndexerPlugin
 {
+    /**
+     * @var bool
+     */
+    private $needInvalidation;
+
+    /**
+     * Check if indexer requires invalidation after store view save
+     *
+     * @param StoreResourceModel $subject
+     * @param AbstractModel $store
+     * @return void
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function beforeSave(StoreResourceModel $subject, AbstractModel $store)
+    {
+        $this->needInvalidation = $store->isObjectNew();
+    }
+
     /**
      * Invalidate indexer on store view save
      *
-     * @param \Magento\Store\Model\ResourceModel\Store $subject
-     * @param \Closure $proceed
-     * @param \Magento\Framework\Model\AbstractModel $store
+     * @param StoreResourceModel $subject
+     * @param StoreResourceModel $result
+     * @return StoreResourceModel
      *
-     * @return \Magento\Store\Model\ResourceModel\Store
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Store\Model\ResourceModel\Store $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $store
-    ) {
-        $needInvalidation = $store->isObjectNew();
-        $result = $proceed($store);
-        if ($needInvalidation) {
-            $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
+    public function afterSave(StoreResourceModel $subject, StoreResourceModel $result)
+    {
+        if ($this->needInvalidation) {
+            $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID)->invalidate();
         }
+
         return $result;
     }
 
     /**
      * Invalidate indexer on store view delete
      *
-     * @param \Magento\Store\Model\ResourceModel\Store $subject
-     * @param \Magento\Store\Model\ResourceModel\Store $result
+     * @param StoreResourceModel $subject
+     * @param StoreResourceModel $result
+     * @return StoreResourceModel
      *
-     * @return \Magento\Store\Model\ResourceModel\Store
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function afterDelete(
-        \Magento\Store\Model\ResourceModel\Store $subject,
-        \Magento\Store\Model\ResourceModel\Store $result
-    ) {
-        $this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
+    public function afterDelete(StoreResourceModel $subject, StoreResourceModel $result)
+    {
+        $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID)->invalidate();
+
         return $result;
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Search/Plugin/CollectionFilter.php b/app/code/Magento/CatalogSearch/Model/Layer/Search/Plugin/CollectionFilter.php
index b2e26dc04236b2528bd8580175e8fa785d63a822..bc50949736116b340accaffe7bea2b4bd4414cc6 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Search/Plugin/CollectionFilter.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Search/Plugin/CollectionFilter.php
@@ -28,19 +28,18 @@ class CollectionFilter
      * Add search filter criteria to search collection
      *
      * @param \Magento\Catalog\Model\Layer\Search\CollectionFilter $subject
-     * @param \Closure $proceed
+     * @param null $result
      * @param \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $collection
      * @param Category $category
      * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundFilter(
+    public function afterFilter(
         \Magento\Catalog\Model\Layer\Search\CollectionFilter $subject,
-        \Closure $proceed,
+        $result,
         $collection,
         Category $category
     ) {
-        $proceed($collection, $category);
         /** @var \Magento\Search\Model\Query $query */
         $query = $this->queryFactory->get();
         if (!$query->isQueryTextShort()) {
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Block/Plugin/FrontTabPluginTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Block/Plugin/FrontTabPluginTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e108def990bfecb8f12b6d07bc09e49f758e22fe
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Block/Plugin/FrontTabPluginTest.php
@@ -0,0 +1,131 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Test\Unit\Block\Plugin;
+
+use Magento\CatalogSearch\Block\Plugin\FrontTabPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\CatalogSearch\Model\Source\Weight as WeightSource;
+use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Front as ProductAttributeFrontTabBlock;
+use Magento\Framework\Data\Form;
+use Magento\Framework\Data\Form\Element\Fieldset;
+use Magento\Framework\Data\Form\Element\AbstractElement;
+use Magento\Framework\View\Element\AbstractBlock;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class FrontTabPluginTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var FrontTabPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var WeightSource|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $weightSourceMock;
+
+    /**
+     * @var ProductAttributeFrontTabBlock|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Form|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $formMock;
+
+    /**
+     * @var Fieldset|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $fieldsetMock;
+
+    /**
+     * @var AbstractElement|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $childElementMock;
+
+    /**
+     * @var AbstractBlock|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $childBlockMock;
+
+    protected function setUp()
+    {
+        $this->weightSourceMock = $this->getMockBuilder(WeightSource::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->subjectMock = $this->getMockBuilder(ProductAttributeFrontTabBlock::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->formMock = $this->getMockBuilder(Form::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->fieldsetMock = $this->getMockBuilder(Fieldset::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->childElementMock = $this->getMockBuilder(AbstractElement::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $this->childBlockMock = $this->getMockBuilder(AbstractBlock::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['addFieldMap', 'addFieldDependence'])
+            ->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            FrontTabPlugin::class,
+            ['weightSource' => $this->weightSourceMock]
+        );
+    }
+
+    public function testBeforeSetForm()
+    {
+        $weightOptions = [1 => '1', 2 => '2'];
+
+        $this->formMock->expects(static::any())
+            ->method('getElement')
+            ->with('front_fieldset')
+            ->willReturn($this->fieldsetMock);
+        $this->weightSourceMock->expects(static::any())
+            ->method('getOptions')
+            ->willReturn($weightOptions);
+        $this->fieldsetMock->expects(static::once())
+            ->method('addField')
+            ->with(
+                'search_weight',
+                'select',
+                [
+                    'name' => 'search_weight',
+                    'label' => __('Search Weight'),
+                    'values' => $weightOptions
+                ],
+                'is_searchable',
+                false
+            )
+            ->willReturn($this->childElementMock);
+        $this->subjectMock->expects(static::any())
+            ->method('getChildBlock')
+            ->with('form_after')
+            ->willReturn($this->childBlockMock);
+        $this->childBlockMock->expects(static::once())
+            ->method('addFieldMap')
+            ->with('search_weight', 'search_weight')
+            ->willReturnSelf();
+        $this->childBlockMock->expects(static::once())
+            ->method('addFieldDependence')
+            ->with('search_weight', 'searchable', '1')
+            ->willReturnSelf();
+
+        $this->plugin->beforeSetForm($this->subjectMock, $this->formMock);
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php
index 086b2606457bab80a79e19022743e525357278c8..6375b659fc76c7bb80d89d65bdd0a64d0a220376 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php
@@ -5,7 +5,8 @@
  */
 namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Fulltext\Plugin;
 
-use \Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Attribute;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Attribute;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class AttributeTest extends \PHPUnit_Framework_TestCase
 {
@@ -24,13 +25,29 @@ class AttributeTest extends \PHPUnit_Framework_TestCase
      */
     protected $indexerRegistryMock;
 
+    /**
+     * @var \Magento\Catalog\Model\ResourceModel\Attribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $attributeMock;
+
     /**
      * @var Attribute
      */
     protected $model;
 
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var \Magento\Framework\Search\Request\Config|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $config;
+
     protected function setUp()
     {
+        $this->objectManager = new ObjectManager($this);
         $this->subjectMock = $this->getMock(\Magento\Catalog\Model\ResourceModel\Attribute::class, [], [], '', false);
         $this->indexerMock = $this->getMockForAbstractClass(
             \Magento\Framework\Indexer\IndexerInterface::class,
@@ -48,113 +65,117 @@ class AttributeTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $this->attributeMock = $this->getMock(
+            \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class,
+            ['dataHasChangedFor', 'isObjectNew', 'getIsSearchable'],
+            [],
+            '',
+            false
+        );
         $this->config =  $this->getMockBuilder(\Magento\Framework\Search\Request\Config::class)
             ->disableOriginalConstructor()
+            ->setMethods(['reset'])
             ->getMock();
-        $this->model = new Attribute($this->indexerRegistryMock, $this->config);
+        $this->model = $this->objectManager->getObject(
+            Attribute::class,
+            [
+                'indexerRegistry' => $this->indexerRegistryMock,
+                'config' => $this->config
+            ]
+        );
     }
 
-    /**
-     * @param bool $isObjectNew
-     * @param bool $isSearchableChanged
-     * @param int $invalidateCounter
-     * @return void
-     * @dataProvider aroundSaveDataProvider
-     */
-    public function testAroundSave($isObjectNew, $isSearchableChanged, $invalidateCounter)
+    public function testBeforeSave()
     {
-        $attributeMock = $this->getMock(
-            \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $attributeMock->expects($this->any())
+        $this->attributeMock->expects($this->once())
+            ->method('isObjectNew')
+            ->willReturn(true);
+        $this->attributeMock->expects($this->once())
             ->method('dataHasChangedFor')
-            ->will($this->returnValue($isSearchableChanged));
+            ->with('is_searchable')
+            ->willReturn(true);
+        $this->assertEquals(
+            null,
+            $this->model->beforeSave($this->subjectMock, $this->attributeMock)
+        );
+    }
 
-        $attributeMock->expects($this->any())->method('isObjectNew')->will($this->returnValue($isObjectNew));
+    public function testAfterSaveNoInvalidation()
+    {
+        $this->assertEquals(
+            $this->subjectMock,
+            $this->model->afterSave($this->subjectMock, $this->subjectMock)
+        );
+    }
 
-        $closureMock = function (\Magento\Catalog\Model\ResourceModel\Eav\Attribute $object) use ($attributeMock) {
-            $this->assertEquals($object, $attributeMock);
-            return $this->subjectMock;
-        };
+    public function testAfterSaveWithInvalidation()
+    {
+        $model = $this->objectManager->getObject(
+            Attribute::class,
+            [
+                'indexerRegistry' => $this->indexerRegistryMock,
+                'config' => $this->config,
+                'saveNeedInvalidation' => true,
+                'saveIsNew' => true
+            ]
+        );
 
-        $this->indexerMock->expects($this->exactly($invalidateCounter))->method('invalidate');
-        $this->prepareIndexer($invalidateCounter);
+        $this->indexerMock->expects($this->once())->method('invalidate');
+        $this->prepareIndexer();
+        $this->config->expects($this->once())
+            ->method('reset');
 
         $this->assertEquals(
             $this->subjectMock,
-            $this->model->aroundSave($this->subjectMock, $closureMock, $attributeMock)
+            $model->afterSave($this->subjectMock, $this->subjectMock)
         );
     }
 
-    /**
-     * @return array
-     */
-    public function aroundSaveDataProvider()
+    public function testBeforeDelete()
     {
-        return [
-            [false, false, 0],
-            [false, true, 1],
-            [true, false, 0],
-            [true, true, 0],
-        ];
+        $this->attributeMock->expects($this->once())
+            ->method('isObjectNew')
+            ->willReturn(false);
+        $this->attributeMock->expects($this->once())
+            ->method('getIsSearchable')
+            ->willReturn(true);
+        $this->assertEquals(
+            null,
+            $this->model->beforeDelete($this->subjectMock, $this->attributeMock)
+        );
     }
 
-    /**
-     * @param bool $isObjectNew
-     * @param bool $isSearchable
-     * @param int $invalidateCounter
-     * @return void
-     * @dataProvider aroundDeleteDataProvider
-     */
-    public function testAroundDelete($isObjectNew, $isSearchable, $invalidateCounter)
+    public function testAfterDeleteNoInvalidation()
     {
-        $attributeMock = $this->getMock(
-            \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class,
-            ['getIsSearchable', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
+        $this->assertEquals(
+            $this->subjectMock,
+            $this->model->afterDelete($this->subjectMock, $this->subjectMock)
         );
-        $attributeMock->expects($this->any())->method('getIsSearchable')->will($this->returnValue($isSearchable));
-        $attributeMock->expects($this->once())->method('isObjectNew')->will($this->returnValue($isObjectNew));
+    }
 
-        $closureMock = function (\Magento\Catalog\Model\ResourceModel\Eav\Attribute $object) use ($attributeMock) {
-            $this->assertEquals($object, $attributeMock);
-            return $this->subjectMock;
-        };
+    public function testAfterDeleteWithInvalidation()
+    {
+        $model = $this->objectManager->getObject(
+            Attribute::class,
+            [
+                'indexerRegistry' => $this->indexerRegistryMock,
+                'config' => $this->config,
+                'deleteNeedInvalidation' => true
+            ]
+        );
 
-        $this->indexerMock->expects($this->exactly($invalidateCounter))->method('invalidate');
-        $this->prepareIndexer($invalidateCounter);
+        $this->indexerMock->expects($this->once())->method('invalidate');
+        $this->prepareIndexer();
 
         $this->assertEquals(
             $this->subjectMock,
-            $this->model->aroundDelete($this->subjectMock, $closureMock, $attributeMock)
+            $model->afterDelete($this->subjectMock, $this->subjectMock)
         );
     }
 
-    /**
-     * @return array
-     */
-    public function aroundDeleteDataProvider()
-    {
-        return [
-            [false, false, 0],
-            [false, true, 1],
-            [true, false, 0],
-            [true, true, 0],
-        ];
-    }
-
-    /**
-     * @param $invalidateCounter
-     */
-    protected function prepareIndexer($invalidateCounter)
+    private function prepareIndexer()
     {
-        $this->indexerRegistryMock->expects($this->exactly($invalidateCounter))
+        $this->indexerRegistryMock->expects($this->once())
             ->method('get')
             ->with(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)
             ->will($this->returnValue($this->indexerMock));
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Product/ActionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Product/ActionTest.php
index 3ffa164d149b875196df8448682eecd1a3ae53a1..f6767c3fc9d1c62a4d3c06b6e984135f168d177b 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Product/ActionTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Product/ActionTest.php
@@ -3,132 +3,122 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Fulltext\Plugin\Product;
 
-use \Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Action;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Action as ProductActionIndexerPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Catalog\Model\Product\Action as ProductAction;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
 class ActionTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var ProductActionIndexerPlugin
      */
-    protected $indexerMock;
+    private $plugin;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Product\Action
+     * @var ObjectManagerHelper
      */
-    protected $subjectMock;
+    private $objectManagerHelper;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerRegistryMock;
+    private $indexerRegistryMock;
 
     /**
-     * @var Action
+     * @var IndexerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $model;
+    private $indexerMock;
+
+    /**
+     * @var ProductAction|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
 
     protected function setUp()
     {
-        $this->subjectMock = $this->getMock(\Magento\Catalog\Model\Product\Action::class, [], [], '', false);
-
-        $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
-            [],
-            '',
-            false,
-            false,
-            true,
-            ['getId', 'getState', '__wakeup']
-        );
-        $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
-            ['get'],
-            [],
-            '',
-            false
-        );
+        $this->indexerRegistryMock = $this->getMockBuilder(IndexerRegistry::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->indexerMock = $this->getMockBuilder(IndexerInterface::class)
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(ProductAction::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->indexerRegistryMock->expects(static::once())
+            ->method('get')
+            ->with(FulltextIndexer::INDEXER_ID)
+            ->willReturn($this->indexerMock);
 
-        $this->model = new Action($this->indexerRegistryMock);
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            ProductActionIndexerPlugin::class,
+            ['indexerRegistry' => $this->indexerRegistryMock]
+        );
     }
 
-    public function testAroundUpdateAttributesNonScheduled()
+    public function testAfterUpdateAttributesNonScheduled()
     {
-        $this->indexerMock->expects($this->once())->method('isScheduled')->will($this->returnValue(false));
-        $this->indexerMock->expects($this->once())->method('reindexList')->with([1, 2, 3]);
-        $this->prepareIndexer();
-
-        $closureMock = function ($productIds, $attrData, $storeId) {
-            $this->assertEquals([1, 2, 3], $productIds);
-            $this->assertEquals([4, 5, 6], $attrData);
-            $this->assertEquals(1, $storeId);
-            return $this->subjectMock;
-        };
-
-        $this->assertEquals(
+        $productIds = [1, 2, 3];
+
+        $this->indexerMock->expects(static::once())
+            ->method('isScheduled')
+            ->willReturn(false);
+        $this->indexerMock->expects(static::once())
+            ->method('reindexList')
+            ->with($productIds);
+
+        $this->assertSame(
             $this->subjectMock,
-            $this->model->aroundUpdateAttributes($this->subjectMock, $closureMock, [1, 2, 3], [4, 5, 6], 1)
+            $this->plugin->afterUpdateAttributes($this->subjectMock, $this->subjectMock, $productIds, [], null)
         );
     }
 
-    public function testAroundUpdateAttributesScheduled()
+    public function testAfterUpdateAttributesScheduled()
     {
-        $this->indexerMock->expects($this->once())->method('isScheduled')->will($this->returnValue(true));
-        $this->indexerMock->expects($this->never())->method('reindexList');
-        $this->prepareIndexer();
-
-        $closureMock = function ($productIds, $attrData, $storeId) {
-            $this->assertEquals([1, 2, 3], $productIds);
-            $this->assertEquals([4, 5, 6], $attrData);
-            $this->assertEquals(1, $storeId);
-            return $this->subjectMock;
-        };
-
-        $this->assertEquals(
+        $productIds = [1, 2, 3];
+
+        $this->indexerMock->expects(static::once())
+            ->method('isScheduled')
+            ->willReturn(true);
+        $this->indexerMock->expects(static::never())
+            ->method('reindexList');
+
+        $this->assertSame(
             $this->subjectMock,
-            $this->model->aroundUpdateAttributes($this->subjectMock, $closureMock, [1, 2, 3], [4, 5, 6], 1)
+            $this->plugin->afterUpdateAttributes($this->subjectMock, $this->subjectMock, $productIds, [], null)
         );
     }
 
-    public function testAroundUpdateWebsitesNonScheduled()
+    public function testAfterUpdateWebsitesNonScheduled()
     {
-        $this->indexerMock->expects($this->once())->method('isScheduled')->will($this->returnValue(false));
-        $this->indexerMock->expects($this->once())->method('reindexList')->with([1, 2, 3]);
-        $this->prepareIndexer();
-
-        $closureMock = function ($productIds, $websiteIds, $type) {
-            $this->assertEquals([1, 2, 3], $productIds);
-            $this->assertEquals([4, 5, 6], $websiteIds);
-            $this->assertEquals('type', $type);
-            return $this->subjectMock;
-        };
-
-        $this->model->aroundUpdateWebsites($this->subjectMock, $closureMock, [1, 2, 3], [4, 5, 6], 'type');
-    }
+        $productIds = [1, 2, 3];
 
-    public function testAroundUpdateWebsitesScheduled()
-    {
-        $this->indexerMock->expects($this->once())->method('isScheduled')->will($this->returnValue(true));
-        $this->indexerMock->expects($this->never())->method('reindexList');
-        $this->prepareIndexer();
-
-        $closureMock = function ($productIds, $websiteIds, $type) {
-            $this->assertEquals([1, 2, 3], $productIds);
-            $this->assertEquals([4, 5, 6], $websiteIds);
-            $this->assertEquals('type', $type);
-            return $this->subjectMock;
-        };
-
-        $this->model->aroundUpdateWebsites($this->subjectMock, $closureMock, [1, 2, 3], [4, 5, 6], 'type');
+        $this->indexerMock->expects(static::once())
+            ->method('isScheduled')
+            ->willReturn(false);
+        $this->indexerMock->expects(static::once())
+            ->method('reindexList')
+            ->with($productIds);
+
+        $this->plugin->afterUpdateWebsites($this->subjectMock, $this->subjectMock, $productIds, [], null);
     }
 
-    protected function prepareIndexer()
+    public function testAfterUpdateWebsitesScheduled()
     {
-        $this->indexerRegistryMock->expects($this->once())
-            ->method('get')
-            ->with(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
+        $productIds = [1, 2, 3];
+
+        $this->indexerMock->expects(static::once())
+            ->method('isScheduled')
+            ->willReturn(true);
+        $this->indexerMock->expects(static::never())
+            ->method('reindexList');
+
+        $this->plugin->afterUpdateWebsites($this->subjectMock, $this->subjectMock, $productIds, [], null);
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/GroupTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/GroupTest.php
index eb6328f249d621b77b923e20b8a5f5ffae0304bc..2fccfb7743de4476565416591dba80a94546f227 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/GroupTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/GroupTest.php
@@ -5,50 +5,66 @@
  */
 namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Fulltext\Plugin\Store;
 
-use \Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\Group;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\Group as StoreGroupIndexerPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Store\Model\ResourceModel\Group as StoreGroupResourceModel;
+use Magento\Store\Model\Group as StoreGroup;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
 class GroupTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var StoreGroupIndexerPlugin
      */
-    protected $indexerMock;
+    private $plugin;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\ResourceModel\Group
+     * @var ObjectManagerHelper
      */
-    protected $subjectMock;
+    private $objectManagerHelper;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerRegistryMock;
+    private $indexerRegistryMock;
 
     /**
-     * @var Group
+     * @var IndexerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $model;
+    private $indexerMock;
+
+    /**
+     * @var StoreGroupResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var StoreGroup|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeGroupMock;
 
     protected function setUp()
     {
-        $this->subjectMock = $this->getMock(\Magento\Store\Model\ResourceModel\Group::class, [], [], '', false);
-        $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
-            [],
-            '',
-            false,
-            false,
-            true,
-            ['getId', 'getState', '__wakeup']
-        );
-        $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
-            ['get'],
-            [],
-            '',
-            false
+        $this->indexerRegistryMock = $this->getMockBuilder(IndexerRegistry::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->indexerMock = $this->getMockBuilder(IndexerInterface::class)
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(StoreGroupResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->storeGroupMock = $this->getMockBuilder(StoreGroup::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['dataHasChangedFor', 'isObjectNew'])
+            ->getMock();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            StoreGroupIndexerPlugin::class,
+            ['indexerRegistry' => $this->indexerRegistryMock]
         );
-        $this->model = new Group($this->indexerRegistryMock);
     }
 
     /**
@@ -56,72 +72,58 @@ class GroupTest extends \PHPUnit_Framework_TestCase
      * @param bool $websiteChanged
      * @param int $invalidateCounter
      * @return void
-     * @dataProvider aroundSaveDataProvider
+     * @dataProvider beforeAfterSaveDataProvider
      */
-    public function testAroundSave($isObjectNew, $websiteChanged, $invalidateCounter)
+    public function testBeforeAfterSave($isObjectNew, $websiteChanged, $invalidateCounter)
     {
-        $groupMock = $this->getMock(
-            \Magento\Store\Model\Group::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $groupMock->expects($this->any())
+        $this->prepareIndexer($invalidateCounter);
+        $this->storeGroupMock->expects(static::any())
             ->method('dataHasChangedFor')
             ->with('website_id')
-            ->will($this->returnValue($websiteChanged));
-        $groupMock->expects($this->once())->method('isObjectNew')->will($this->returnValue($isObjectNew));
+            ->willReturn($websiteChanged);
+        $this->storeGroupMock->expects(static::once())
+            ->method('isObjectNew')
+            ->willReturn($isObjectNew);
+        $this->indexerMock->expects(static::exactly($invalidateCounter))
+            ->method('invalidate');
 
-        $closureMock = function (\Magento\Store\Model\Group $object) use ($groupMock) {
-            $this->assertEquals($object, $groupMock);
-            return $this->subjectMock;
-        };
-
-        $this->indexerMock->expects($this->exactly($invalidateCounter))->method('invalidate');
-        $this->prepareIndexer($invalidateCounter);
-
-        $this->assertEquals(
-            $this->subjectMock,
-            $this->model->aroundSave($this->subjectMock, $closureMock, $groupMock)
-        );
+        $this->plugin->beforeSave($this->subjectMock, $this->storeGroupMock);
+        $this->assertSame($this->subjectMock, $this->plugin->afterSave($this->subjectMock, $this->subjectMock));
     }
 
     /**
      * @return array
      */
-    public function aroundSaveDataProvider()
+    public function beforeAfterSaveDataProvider()
     {
         return [
             [false, false, 0],
             [false, true, 1],
             [true, false, 0],
-            [true, true, 0],
+            [true, true, 0]
         ];
     }
 
-    /**
-     * @return void
-     */
     public function testAfterDelete()
     {
-        $this->indexerMock->expects($this->once())->method('invalidate');
         $this->prepareIndexer(1);
+        $this->indexerMock->expects(static::once())
+            ->method('invalidate');
 
-        $this->assertEquals(
-            $this->subjectMock,
-            $this->model->afterDelete($this->subjectMock, $this->subjectMock)
-        );
+        $this->assertSame($this->subjectMock, $this->plugin->afterDelete($this->subjectMock, $this->subjectMock));
     }
 
     /**
+     * Prepare expectations for indexer
+     * 
      * @param int $invalidateCounter
+     * @return void
      */
-    protected function prepareIndexer($invalidateCounter)
+    private function prepareIndexer($invalidateCounter)
     {
-        $this->indexerRegistryMock->expects($this->exactly($invalidateCounter))
+        $this->indexerRegistryMock->expects(static::exactly($invalidateCounter))
             ->method('get')
-            ->with(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
+            ->with(FulltextIndexer::INDEXER_ID)
+            ->willReturn($this->indexerMock);
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/ViewTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/ViewTest.php
index 22239bb0efcaf3b48e2c221c990b9f17252bb316..d5db837eaa2bd77f29eab8984fda1ef3b001b8ad 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/ViewTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/Store/ViewTest.php
@@ -5,116 +5,118 @@
  */
 namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Fulltext\Plugin\Store;
 
-use \Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\View;
+use Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\View as StoreViewIndexerPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Store\Model\ResourceModel\Store as StoreResourceModel;
+use Magento\Store\Model\Store;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
 
 class ViewTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Indexer\IndexerInterface
+     * @var StoreViewIndexerPlugin
      */
-    protected $indexerMock;
+    private $plugin;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\ResourceModel\Store
+     * @var ObjectManagerHelper
      */
-    protected $subjectMock;
+    private $objectManagerHelper;
 
     /**
-     * @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     * @var IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $indexerRegistryMock;
+    private $indexerRegistryMock;
 
     /**
-     * @var View
+     * @var IndexerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $model;
+    private $indexerMock;
+
+    /**
+     * @var StoreResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Store|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeMock;
 
     protected function setUp()
     {
-        $this->subjectMock = $this->getMock(\Magento\Store\Model\ResourceModel\Store::class, [], [], '', false);
-        $this->indexerMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Indexer\IndexerInterface::class,
-            [],
-            '',
-            false,
-            false,
-            true,
-            ['getId', 'getState', '__wakeup']
-        );
-        $this->indexerRegistryMock = $this->getMock(
-            \Magento\Framework\Indexer\IndexerRegistry::class,
-            ['get'],
-            [],
-            '',
-            false
+        $this->indexerRegistryMock = $this->getMockBuilder(IndexerRegistry::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->indexerMock = $this->getMockBuilder(IndexerInterface::class)
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(StoreResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->storeMock = $this->getMockBuilder(Store::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['isObjectNew'])
+            ->getMock();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            StoreViewIndexerPlugin::class,
+            ['indexerRegistry' => $this->indexerRegistryMock]
         );
-        $this->model = new View($this->indexerRegistryMock);
     }
 
     /**
      * @param bool $isObjectNew
      * @param int $invalidateCounter
-     * @return void
-     * @dataProvider aroundSaveDataProvider
+     *
+     * @dataProvider beforeAfterSaveDataProvider
      */
-    public function testAroundSave($isObjectNew, $invalidateCounter)
+    public function testBeforeAfterSave($isObjectNew, $invalidateCounter)
     {
-        $viewMock = $this->getMock(
-            \Magento\Store\Model\Store::class,
-            ['dataHasChangedFor', 'isObjectNew', '__wakeup'],
-            [],
-            '',
-            false
-        );
-        $viewMock->expects($this->once())->method('isObjectNew')->will($this->returnValue($isObjectNew));
-
-        $closureMock = function (\Magento\Store\Model\Store $object) use ($viewMock) {
-            $this->assertEquals($object, $viewMock);
-            return $this->subjectMock;
-        };
-
-        $this->indexerMock->expects($this->exactly($invalidateCounter))->method('invalidate');
         $this->prepareIndexer($invalidateCounter);
+        $this->storeMock->expects(static::once())
+            ->method('isObjectNew')
+            ->willReturn($isObjectNew);
+        $this->indexerMock->expects(static::exactly($invalidateCounter))
+            ->method('invalidate');
 
-        $this->assertEquals(
-            $this->subjectMock,
-            $this->model->aroundSave($this->subjectMock, $closureMock, $viewMock)
-        );
+        $this->plugin->beforeSave($this->subjectMock, $this->storeMock);
+        $this->assertSame($this->subjectMock, $this->plugin->afterSave($this->subjectMock, $this->subjectMock));
     }
 
     /**
      * @return array
      */
-    public function aroundSaveDataProvider()
+    public function beforeAfterSaveDataProvider()
     {
         return [
             [false, 0],
-            [true, 1],
+            [true, 1]
         ];
     }
 
-    /**
-     * @return void
-     */
     public function testAfterDelete()
     {
-        $this->indexerMock->expects($this->once())->method('invalidate');
         $this->prepareIndexer(1);
+        $this->indexerMock->expects(static::once())
+            ->method('invalidate');
 
-        $this->assertEquals(
-            $this->subjectMock,
-            $this->model->afterDelete($this->subjectMock, $this->subjectMock)
-        );
+        $this->assertSame($this->subjectMock, $this->plugin->afterDelete($this->subjectMock, $this->subjectMock));
     }
 
     /**
+     * Prepare expectations for indexer
+     *
      * @param int $invalidateCounter
+     * @return void
      */
-    protected function prepareIndexer($invalidateCounter)
+    private function prepareIndexer($invalidateCounter)
     {
-        $this->indexerRegistryMock->expects($this->exactly($invalidateCounter))
+        $this->indexerRegistryMock->expects(static::exactly($invalidateCounter))
             ->method('get')
-            ->with(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)
-            ->will($this->returnValue($this->indexerMock));
+            ->with(FulltextIndexer::INDEXER_ID)
+            ->willReturn($this->indexerMock);
     }
 }
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Search/Plugin/CollectionFilterTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Search/Plugin/CollectionFilterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6ba226c572dfe9866e6fad9cf6b83037d185e3f7
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Search/Plugin/CollectionFilterTest.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Test\Unit\Model\Layer\Search\Plugin;
+
+use Magento\CatalogSearch\Model\Layer\Search\Plugin\CollectionFilter as CollectionFilterPlugin;
+use Magento\Catalog\Model\Layer\Search\CollectionFilter;
+use Magento\Catalog\Model\Category;
+use Magento\Search\Model\QueryFactory;
+use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Search\Model\Query;
+
+class CollectionFilterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var CollectionFilterPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var Collection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $collectionMock;
+
+    /**
+     * @var Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryMock;
+
+    /**
+     * @var QueryFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $queryFactoryMock;
+
+    /**
+     * @var CollectionFilter|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $collectionFilterMock;
+
+    /**
+     * @var Query|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $queryMock;
+
+    /***
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+
+        $this->collectionMock = $this->getMockBuilder(Collection::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['addSearchFilter'])
+            ->getMock();
+        $this->categoryMock = $this->getMockBuilder(Category::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->queryFactoryMock = $this->getMockBuilder(QueryFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['get'])
+            ->getMock();
+        $this->collectionFilterMock = $this->getMockBuilder(CollectionFilter::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->queryMock = $this->getMockBuilder(Query::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['isQueryTextShort', 'getQueryText'])
+            ->getMock();
+
+        $this->plugin = $this->objectManager->getObject(
+            CollectionFilterPlugin::class,
+            ['queryFactory' => $this->queryFactoryMock]
+        );
+    }
+
+    public function testAfterFilter()
+    {
+        $queryText = 'Test Query';
+
+        $this->queryFactoryMock->expects($this->once())
+            ->method('get')
+            ->willReturn($this->queryMock);
+        $this->queryMock->expects($this->once())
+            ->method('isQueryTextShort')
+            ->willReturn(false);
+        $this->queryMock->expects($this->once())
+            ->method('getQueryText')
+            ->willReturn($queryText);
+        $this->collectionMock->expects($this->once())
+            ->method('addSearchFilter')
+            ->with($queryText);
+
+        $this->plugin->afterFilter(
+            $this->collectionFilterMock,
+            null,
+            $this->collectionMock,
+            $this->categoryMock
+        );
+    }
+}
diff --git a/lib/internal/Magento/Framework/Interception/Chain/Chain.php b/lib/internal/Magento/Framework/Interception/Chain/Chain.php
deleted file mode 100644
index 34a8308e021e86cfe85066462ca6ca22bd1cb79b..0000000000000000000000000000000000000000
--- a/lib/internal/Magento/Framework/Interception/Chain/Chain.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2016 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Framework\Interception\Chain;
-
-use Magento\Framework\Interception\InterceptorInterface;
-use Magento\Framework\Interception\DefinitionInterface;
-use Magento\Framework\Interception\PluginListInterface;
-
-class Chain implements \Magento\Framework\Interception\ChainInterface
-{
-    /**
-     * @var \Magento\Framework\Interception\PluginListInterface
-     */
-    protected $pluginList;
-
-    /**
-     * @param PluginListInterface $pluginList
-     */
-    public function __construct(PluginListInterface $pluginList)
-    {
-        $this->pluginList = $pluginList;
-    }
-
-    /**
-     * Invoke next plugin in chain
-     *
-     * @param string $type
-     * @param string $method
-     * @param string $previousPluginCode
-     * @param InterceptorInterface $subject
-     * @param array $arguments
-     * @return mixed|void
-     */
-    public function invokeNext(
-        $type,
-        $method,
-        InterceptorInterface $subject,
-        array $arguments,
-        $previousPluginCode = null
-    ) {
-        $pluginInfo = $this->pluginList->getNext($type, $method, $previousPluginCode);
-        $capMethod = ucfirst($method);
-        $result = null;
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_BEFORE])) {
-            foreach ($pluginInfo[DefinitionInterface::LISTENER_BEFORE] as $code) {
-                $pluginInstance = $this->pluginList->getPlugin($type, $code);
-                $pluginMethod = 'before' . $capMethod;
-                $beforeResult = $pluginInstance->$pluginMethod($subject, ...array_values($arguments));
-                if ($beforeResult) {
-                    $arguments = $beforeResult;
-                }
-                unset($pluginInstance, $pluginMethod);
-            }
-        }
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_AROUND])) {
-            $chain = $this;
-            $code = $pluginInfo[DefinitionInterface::LISTENER_AROUND];
-            $next = function () use ($chain, $type, $method, $subject, $code) {
-                return $chain->invokeNext($type, $method, $subject, func_get_args(), $code);
-            };
-            $pluginInstance = $this->pluginList->getPlugin($type, $code);
-            $pluginMethod = 'around' . $capMethod;
-            $result = $pluginInstance->$pluginMethod($subject, $next, ...array_values($arguments));
-            unset($pluginInstance, $pluginMethod);
-        } else {
-            $result = $subject->___callParent($method, $arguments);
-        }
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_AFTER])) {
-            foreach ($pluginInfo[DefinitionInterface::LISTENER_AFTER] as $code) {
-                $result = $this->pluginList->getPlugin($type, $code)->{'after' . $capMethod}($subject, $result);
-            }
-        }
-        return $result;
-    }
-}
diff --git a/lib/internal/Magento/Framework/Interception/ChainInterface.php b/lib/internal/Magento/Framework/Interception/ChainInterface.php
deleted file mode 100644
index 648175b63038e5c929a60940c1c00300284e5c40..0000000000000000000000000000000000000000
--- a/lib/internal/Magento/Framework/Interception/ChainInterface.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2016 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Framework\Interception;
-
-interface ChainInterface
-{
-    /**
-     * @param string $type
-     * @param string $method
-     * @param InterceptorInterface $subject
-     * @param array $arguments
-     * @param string $previousPluginCode
-     * @return mixed
-     */
-    public function invokeNext(
-        $type,
-        $method,
-        InterceptorInterface $subject,
-        array $arguments,
-        $previousPluginCode = null
-    );
-}
diff --git a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
index 438589cd51172933d329926aa76f1363d71a1cda..1ff11552533b1e6514409402e778687483ae6d18 100644
--- a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
+++ b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
@@ -49,7 +49,6 @@ class InterfaceValidator
         $plugin = new \ReflectionClass($pluginClass);
         $type = new \ReflectionClass($interceptedType);
 
-        $pluginMethods = [];
         foreach ($plugin->getMethods(\ReflectionMethod::IS_PUBLIC) as $pluginMethod) {
             /** @var  $pluginMethod \ReflectionMethod */
             $originMethodName = $this->getOriginMethodName($pluginMethod->getName());
@@ -112,12 +111,15 @@ class InterfaceValidator
                     );
                     break;
                 case self::METHOD_AFTER:
+                    // TODO: Remove this condition check in scope of MAGETWO-56123
                     if (count($pluginMethodParameters) > 1) {
-                        throw new ValidatorException(
-                            new Phrase(
-                                'Invalid method signature. Detected extra parameters in %1::%2',
-                                [$pluginClass, $pluginMethod->getName()]
-                            )
+                        // remove result
+                        array_shift($pluginMethodParameters);
+                        $this->validateMethodsParameters(
+                            $pluginMethodParameters,
+                            $originMethodParameters,
+                            $pluginClass,
+                            $pluginMethod->getName()
                         );
                     }
                     break;
diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php
index 1ef8a0d9dc22f9018894c9aa94aa2e6eec762778..9aa00e1c751756a93cae6877c14f0337d38c8028 100644
--- a/lib/internal/Magento/Framework/Interception/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Interceptor.php
@@ -18,33 +18,19 @@ use Magento\Framework\App\ObjectManager;
  */
 trait Interceptor
 {
-    /**
-     * Object Manager instance
-     *
-     * @var \Magento\Framework\ObjectManagerInterface
-     */
-    protected $pluginLocator = null;
-
     /**
      * List of plugins
      *
-     * @var \Magento\Framework\Interception\PluginListInterface
+     * @var PluginListInterface
      */
-    protected $pluginList = null;
-
-    /**
-     * Invocation chain
-     *
-     * @var \Magento\Framework\Interception\ChainInterface
-     */
-    protected $chain = null;
+    private $pluginList;
 
     /**
      * Subject type name
      *
      * @var string
      */
-    protected $subjectType = null;
+    private $subjectType;
 
     /**
      * Initialize the Interceptor
@@ -53,9 +39,7 @@ trait Interceptor
      */
     public function ___init()
     {
-        $this->pluginLocator = ObjectManager::getInstance();
-        $this->pluginList = $this->pluginLocator->get(\Magento\Framework\Interception\PluginListInterface::class);
-        $this->chain = $this->pluginLocator->get(\Magento\Framework\Interception\ChainInterface::class);
+        $this->pluginList = ObjectManager::getInstance()->get(PluginListInterface::class);
         $this->subjectType = get_parent_class($this);
         if (method_exists($this->subjectType, '___init')) {
             parent::___init();
@@ -86,7 +70,7 @@ trait Interceptor
         } else {
             $properties = array_keys(get_object_vars($this));
         }
-        $properties = array_diff($properties, ['pluginLocator', 'pluginList', 'chain', 'subjectType', 'pluginLocator']);
+        $properties = array_diff($properties, ['pluginList', 'subjectType']);
         return $properties;
     }
 
@@ -113,45 +97,62 @@ trait Interceptor
      */
     protected function ___callPlugins($method, array $arguments, array $pluginInfo)
     {
-        $capMethod = ucfirst($method);
-        $result = null;
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_BEFORE])) {
-            // Call 'before' listeners
-            foreach ($pluginInfo[DefinitionInterface::LISTENER_BEFORE] as $code) {
-                $pluginInstance = $this->pluginList->getPlugin($this->subjectType, $code);
-                $pluginMethod = 'before' . $capMethod;
-                $beforeResult = $pluginInstance->$pluginMethod($this, ...array_values($arguments));
-                if ($beforeResult) {
-                    $arguments = $beforeResult;
+        $subject = $this;
+        $type = $this->subjectType;
+        $pluginList = $this->pluginList;
+
+        $next = function (...$arguments) use (
+            $method,
+            &$pluginInfo,
+            $subject,
+            $type,
+            $pluginList,
+            &$next
+        ) {
+            $capMethod = ucfirst($method);
+            $currentPluginInfo = $pluginInfo;
+            $result = null;
+
+            if (isset($currentPluginInfo[DefinitionInterface::LISTENER_BEFORE])) {
+                // Call 'before' listeners
+                foreach ($currentPluginInfo[DefinitionInterface::LISTENER_BEFORE] as $code) {
+                    $pluginInstance = $pluginList->getPlugin($type, $code);
+                    $pluginMethod = 'before' . $capMethod;
+                    $beforeResult = $pluginInstance->$pluginMethod($this, ...array_values($arguments));
+
+                    if ($beforeResult !== null) {
+                        $arguments = (array)$beforeResult;
+                    }
                 }
-                unset($pluginInstance, $pluginMethod);
             }
-        }
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_AROUND])) {
-            // Call 'around' listener
-            $chain = $this->chain;
-            $type = $this->subjectType;
-            /** @var \Magento\Framework\Interception\InterceptorInterface $subject */
-            $subject = $this;
-            $code = $pluginInfo[DefinitionInterface::LISTENER_AROUND];
-            $next = function () use ($chain, $type, $method, $subject, $code) {
-                return $chain->invokeNext($type, $method, $subject, func_get_args(), $code);
-            };
-            $pluginInstance = $this->pluginList->getPlugin($this->subjectType, $code);
-            $pluginMethod = 'around' . $capMethod;
-            $result = $pluginInstance->$pluginMethod($this, $next, ...array_values($arguments));
-            unset($pluginInstance, $pluginMethod);
-        } else {
-            // Call original method
-            $result = parent::$method(...array_values($arguments));
-        }
-        if (isset($pluginInfo[DefinitionInterface::LISTENER_AFTER])) {
-            // Call 'after' listeners
-            foreach ($pluginInfo[DefinitionInterface::LISTENER_AFTER] as $code) {
-                $result = $this->pluginList->getPlugin($this->subjectType, $code)
-                    ->{'after' . $capMethod}($this, $result);
+
+            if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AROUND])) {
+                // Call 'around' listener
+                $code = $currentPluginInfo[DefinitionInterface::LISTENER_AROUND];
+                $pluginInfo = $pluginList->getNext($type, $method, $code);
+                $pluginInstance = $pluginList->getPlugin($type, $code);
+                $pluginMethod = 'around' . $capMethod;
+                $result = $pluginInstance->$pluginMethod($subject, $next, ...array_values($arguments));
+            } else {
+                // Call original method
+                $result = $subject->___callParent($method, $arguments);
             }
-        }
+
+            if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AFTER])) {
+                // Call 'after' listeners
+                foreach ($currentPluginInfo[DefinitionInterface::LISTENER_AFTER] as $code) {
+                    $pluginInstance = $pluginList->getPlugin($type, $code);
+                    $pluginMethod = 'after' . $capMethod;
+                    $result = $pluginInstance->$pluginMethod($subject, $result, ...array_values($arguments));
+                }
+            }
+
+            return $result;
+        };
+
+        $result = $next(...array_values($arguments));
+        $next = null;
+
         return $result;
     }
 }
diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Chain/ChainTest.php b/lib/internal/Magento/Framework/Interception/Test/Unit/Chain/ChainTest.php
deleted file mode 100644
index 747ad56520c8c00a96f5108548bc575e17c095ba..0000000000000000000000000000000000000000
--- a/lib/internal/Magento/Framework/Interception/Test/Unit/Chain/ChainTest.php
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-/**
- * Copyright © 2016 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\Framework\Interception\Test\Unit\Chain;
-
-class ChainTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var \Magento\Framework\Interception\Chain\Chain
-     */
-    protected $_model;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $_pluginListMock;
-
-    protected function setUp()
-    {
-        $this->_pluginListMock = $this->getMock(\Magento\Framework\Interception\PluginListInterface::class);
-        $this->_model = new \Magento\Framework\Interception\Chain\Chain($this->_pluginListMock);
-    }
-
-    /**
-     * @covers \Magento\Framework\Interception\Chain\Chain::invokeNext
-     * @covers \Magento\Framework\Interception\Chain\Chain::__construct
-     */
-    public function testInvokeNextBeforePlugin()
-    {
-        $type = 'type';
-        $method = 'method';
-
-        $subjectMock = $this->getMock(\Magento\Framework\Interception\InterceptorInterface::class);
-        $pluginMock = $this->getMock('PluginClass', ['beforeMethod']);
-
-        $pluginMock->expects($this->once())
-            ->method('beforeMethod')
-            ->with($subjectMock, 1, 2)
-            ->will($this->returnValue(['beforeMethodResult']));
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getNext')
-            ->with($type, $method, null)
-            ->will(
-                $this->returnValue(
-                    [\Magento\Framework\Interception\DefinitionInterface::LISTENER_BEFORE => ['code']]
-                )
-            );
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getPlugin')
-            ->with($type, 'code')
-            ->will($this->returnValue($pluginMock));
-
-        $subjectMock->expects($this->once())
-            ->method('___callParent')
-            ->with('method', ['beforeMethodResult'])
-            ->will($this->returnValue('subjectMethodResult'));
-
-        $this->assertEquals('subjectMethodResult', $this->_model->invokeNext($type, $method, $subjectMock, [1, 2]));
-    }
-
-    /**
-     * @covers \Magento\Framework\Interception\Chain\Chain::invokeNext
-     */
-    public function testInvokeNextAroundPlugin()
-    {
-        $type = 'type';
-        $method = 'method';
-
-        $subjectMock = $this->getMock(\Magento\Framework\Interception\InterceptorInterface::class);
-        $pluginMock = $this->getMock('PluginClass', ['aroundMethod']);
-
-        $pluginMock->expects($this->once())
-            ->method('aroundMethod')
-            ->with($this->anything())
-            ->will($this->returnValue('subjectMethodResult'));
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getNext')
-            ->with($type, $method, null)
-            ->will($this->returnValue([
-                \Magento\Framework\Interception\DefinitionInterface::LISTENER_AROUND => 'code',
-            ]));
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getPlugin')
-            ->with($type, 'code')
-            ->will($this->returnValue($pluginMock));
-
-        $this->assertEquals('subjectMethodResult', $this->_model->invokeNext($type, $method, $subjectMock, []));
-    }
-
-    /**
-     * @covers \Magento\Framework\Interception\Chain\Chain::invokeNext
-     */
-    public function testInvokeNextAfterPlugin()
-    {
-        $type = 'type';
-        $method = 'method';
-
-        $subjectMock = $this->getMock(\Magento\Framework\Interception\InterceptorInterface::class);
-        $pluginMock = $this->getMock('PluginClass', ['afterMethod']);
-
-        $pluginMock->expects($this->once())
-            ->method('afterMethod')
-            ->with($subjectMock, 'subjectMethodResult')
-            ->will($this->returnValue('afterMethodResult'));
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getNext')
-            ->with($type, $method, null)
-            ->will(
-                $this->returnValue(
-                    [\Magento\Framework\Interception\DefinitionInterface::LISTENER_AFTER => ['code']]
-                )
-            );
-
-        $this->_pluginListMock->expects($this->once())
-            ->method('getPlugin')
-            ->with($type, 'code')
-            ->will($this->returnValue($pluginMock));
-
-        $subjectMock->expects($this->once())
-            ->method('___callParent')
-            ->with('method', [1, 2])
-            ->will($this->returnValue('subjectMethodResult'));
-
-        $this->assertEquals('afterMethodResult', $this->_model->invokeNext($type, $method, $subjectMock, [1, 2]));
-    }
-}
diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/InterfaceValidatorTest.php b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/InterfaceValidatorTest.php
index fb9a63c5c09e76014986c6439285ea1322fc64c5..a2660a4561fe9167918bf2d9dbae795b633ba953 100644
--- a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/InterfaceValidatorTest.php
+++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/InterfaceValidatorTest.php
@@ -114,7 +114,7 @@ class InterfaceValidatorTest extends \PHPUnit_Framework_TestCase
 
     /**
      * @expectedException \Magento\Framework\Exception\ValidatorException
-     * @expectedExceptionMessage Invalid method signature. Detected extra parameters
+     * @expectedExceptionMessage Invalid method signature. Invalid method parameters count
      * @covers \Magento\Framework\Interception\Code\InterfaceValidator::validate
      */
     public function testValidateExtraParameters()
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php b/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7b3624c501d8da24142923ce66347444a2754e7b
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception;
+
+use Magento\Framework\Interception;
+
+class InterceptorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Sample\Interceptor
+     */
+    private $sampleInterceptor;
+
+    /**
+     * @var array
+     */
+    private $samplePlugins;
+
+    /**
+     * @var Interception\PluginListInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $pluginListMock;
+
+    protected function setUp()
+    {
+        $this->pluginListMock = $this->getMockBuilder(Interception\PluginListInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->sampleInterceptor = new Sample\Interceptor();
+        $this->samplePlugins = [
+            'plugin1' => new Sample\Plugin1(),
+            'plugin2' => new Sample\Plugin2(),
+            'plugin3' => new Sample\Plugin3(),
+            'plugin4' => new Sample\Plugin4()
+        ];
+
+        $this->sampleInterceptor->setPluginList($this->pluginListMock);
+    }
+
+    public function testCallPlugins()
+    {
+        $subjectType = Sample\Entity::class;
+        $method = 'doSomething';
+        $capMethod = ucfirst($method);
+        $pluginMap = [
+            [$subjectType, 'plugin1', $this->samplePlugins['plugin1']],
+            [$subjectType, 'plugin2', $this->samplePlugins['plugin2']],
+            [$subjectType, 'plugin3', $this->samplePlugins['plugin3']],
+            [$subjectType, 'plugin4', $this->samplePlugins['plugin4']]
+        ];
+        $pluginInfoMap = [
+            [
+                $subjectType,
+                $method,
+                null,
+                [
+                    Interception\DefinitionInterface::LISTENER_BEFORE => ['plugin1', 'plugin2'],
+                    Interception\DefinitionInterface::LISTENER_AROUND => 'plugin3',
+                    Interception\DefinitionInterface::LISTENER_AFTER => ['plugin1', 'plugin2', 'plugin3']
+                ]
+            ],
+            [
+                $subjectType,
+                $method,
+                'plugin3',
+                [
+                    Interception\DefinitionInterface::LISTENER_BEFORE => ['plugin4'],
+                    Interception\DefinitionInterface::LISTENER_AROUND => 'plugin4',
+                    Interception\DefinitionInterface::LISTENER_AFTER => ['plugin4']
+                ]
+            ],
+            [
+                $subjectType,
+                $method,
+                'plugin4',
+                null
+            ]
+        ];
+        $expectedPluginCalls = [
+            Sample\Plugin1::class . '::before' . $capMethod,
+            Sample\Plugin2::class . '::before' . $capMethod,
+            Sample\Plugin3::class . '::around' . $capMethod,
+            Sample\Plugin4::class . '::before' . $capMethod,
+            Sample\Plugin4::class . '::around' . $capMethod,
+            Sample\Entity::class . '::' . $method,
+            Sample\Plugin4::class . '::after' . $capMethod,
+            Sample\Plugin1::class . '::after' . $capMethod,
+            Sample\Plugin2::class . '::after' . $capMethod,
+            Sample\Plugin3::class . '::after' . $capMethod
+        ];
+
+        $this->pluginListMock->expects(static::any())
+            ->method('getPlugin')
+            ->willReturnMap($pluginMap);
+        $this->pluginListMock->expects(static::exactly(3))
+            ->method('getNext')
+            ->willReturnMap($pluginInfoMap);
+
+        $this->assertTrue($this->sampleInterceptor->$method());
+        $this->assertEquals($expectedPluginCalls, $this->sampleInterceptor->getPluginCalls());
+    }
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Entity.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Entity.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ceff81795a707c445c3a50888ff93711ba1fefd
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Entity.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+/**
+ * Sample class
+ */
+class Entity
+{
+    /**
+     * @var array
+     */
+    private $pluginCalls = [];
+
+    /**
+     * Sample method
+     *
+     * @return bool
+     */
+    public function doSomething()
+    {
+        $this->addPluginCall(self::class . '::' . __FUNCTION__);
+
+        return true;
+    }
+
+    /**
+     * Get plugin calls info for testing
+     *
+     * @return array
+     */
+    public function getPluginCalls()
+    {
+        return $this->pluginCalls;
+    }
+
+    /**
+     * Add plugin call info for testing
+     *
+     * @param string $call
+     * @return void
+     */
+    public function addPluginCall($call)
+    {
+        $this->pluginCalls[] = $call;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Interceptor.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Interceptor.php
new file mode 100644
index 0000000000000000000000000000000000000000..0e5175ed6e5a2a1adce3e27ee7d1952210693f36
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Interceptor.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+use Magento\Framework\Interception;
+
+/**
+ * Sample interceptor
+ */
+class Interceptor extends Entity implements Interception\InterceptorInterface
+{
+    use Interception\Interceptor;
+
+    public function __construct()
+    {
+        $this->___init();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function ___init()
+    {
+        $this->subjectType = get_parent_class($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function doSomething()
+    {
+        $pluginInfo = $this->pluginList->getNext($this->subjectType, 'doSomething');
+        if (!$pluginInfo) {
+            return parent::doSomething();
+        } else {
+            return $this->___callPlugins('doSomething', func_get_args(), $pluginInfo);
+        }
+    }
+
+    /**
+     * Set plugin list
+     *
+     * @param Interception\PluginListInterface $pluginList
+     * @return void
+     */
+    public function setPluginList(Interception\PluginListInterface $pluginList)
+    {
+        $this->pluginList = $pluginList;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin1.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin1.php
new file mode 100644
index 0000000000000000000000000000000000000000..be3669c79930ee1c58bc93c3c36c71fc88ebb2a1
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin1.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+/**
+ * Sample plugin
+ */
+class Plugin1
+{
+    /**
+     * Sample before-plugin method
+     *
+     * @return void
+     */
+    public function beforeDoSomething(Entity $subject)
+    {
+        $subject->addPluginCall(static::class . '::' . __FUNCTION__);
+        //Not changing arguments
+    }
+
+    /**
+     * Sample around-plugin method
+     *
+     * @param Entity $subject
+     * @param \Closure $proceed
+     * @return mixed
+     */
+    public function aroundDoSomething(Entity $subject, \Closure $proceed)
+    {
+        $subject->addPluginCall(static::class . '::' . __FUNCTION__);
+        //Not breaking the chain
+        return $proceed();
+    }
+
+    /**
+     * Sample after-plugin method
+     *
+     * @param Entity $subject
+     * @param mixed $result
+     * @return mixed
+     */
+    public function afterDoSomething(Entity $subject, $result)
+    {
+        $subject->addPluginCall(static::class . '::' . __FUNCTION__);
+        //Not changing result
+        return $result;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin2.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin2.php
new file mode 100644
index 0000000000000000000000000000000000000000..df4c6d5fc18de37afe45cb9b05e526f6595ce526
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin2.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+/**
+ * Sample plugin
+ */
+class Plugin2 extends Plugin1
+{
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin3.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin3.php
new file mode 100644
index 0000000000000000000000000000000000000000..0bb18890daf25f6e0e9969f1b493765d5496428b
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin3.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+/**
+ * Sample plugin
+ */
+class Plugin3 extends Plugin1
+{
+}
diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin4.php b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin4.php
new file mode 100644
index 0000000000000000000000000000000000000000..02fc7dada8f63ccc92ee1e13f48d1f49a538feb3
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Interception/Sample/Plugin4.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Interception\Sample;
+
+/**
+ * Sample plugin
+ */
+class Plugin4 extends Plugin1
+{
+}