diff --git a/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php
index 31a13191750a3b3097e5b05e6392d07ab1a45abe..2f0102ffd410d608da945ec0ec53fd8e65d1aaf7 100755
--- a/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php
@@ -20,7 +20,8 @@ class BackendModelTest extends \PHPUnit\Framework\TestCase
             \Magento\Backend\Model\Config\SessionLifetime\BackendModel::class
         );
         if ($errorMessage !== null) {
-            $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $errorMessage);
+            $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+            $this->expectExceptionMessage($errorMessage);
         }
         $model->setValue($value);
         $object = $model->beforeSave();
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
index 31e322f4e38f2b2b14a1032c4cdb872b63dd837a..ac2a01f9c5a84078586640edfd0e58de49534736 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
@@ -149,7 +149,7 @@ class Processor
         }
 
         $fileName = \Magento\MediaStorage\Model\File\Uploader::getCorrectFileName($pathinfo['basename']);
-        $dispretionPath = \Magento\MediaStorage\Model\File\Uploader::getDispretionPath($fileName);
+        $dispretionPath = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName);
         $fileName = $dispretionPath . '/' . $fileName;
 
         $fileName = $this->getNotDuplicatedFilename($fileName, $dispretionPath);
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php
index af6c4dba784f0529d93fb791e734378f06c11e63..b54c66d75a05883cf3fa7ba44ad1423b99a4653e 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php
@@ -150,7 +150,7 @@ class ValidatorFile extends Validator
             $extension = pathinfo(strtolower($fileInfo['name']), PATHINFO_EXTENSION);
 
             $fileName = \Magento\MediaStorage\Model\File\Uploader::getCorrectFileName($fileInfo['name']);
-            $dispersion = \Magento\MediaStorage\Model\File\Uploader::getDispretionPath($fileName);
+            $dispersion = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName);
 
             $filePath = $dispersion;
 
diff --git a/app/code/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProducts.php b/app/code/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProducts.php
new file mode 100644
index 0000000000000000000000000000000000000000..342b703ded0a50d409fbed14868af74aa80130a7
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProducts.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Plugin\Model\AttributeSetRepository;
+
+use Magento\Catalog\Model\ResourceModel\Product\Collection;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
+use Magento\Eav\Api\AttributeSetRepositoryInterface;
+use Magento\Eav\Api\Data\AttributeSetInterface;
+
+/**
+ * Delete related products after attribute set successfully removed.
+ */
+class RemoveProducts
+{
+    /**
+     * Retrieve products related to specific attribute set.
+     *
+     * @var CollectionFactory
+     */
+    private $collectionFactory;
+
+    /**
+     * RemoveProducts constructor.
+     *
+     * @param CollectionFactory $collectionFactory
+     */
+    public function __construct(CollectionFactory $collectionFactory)
+    {
+        $this->collectionFactory = $collectionFactory;
+    }
+
+    /**
+     * Delete related to specific attribute set products, if attribute set was removed successfully.
+     *
+     * @param AttributeSetRepositoryInterface $subject
+     * @param bool $result
+     * @param AttributeSetInterface $attributeSet
+     * @return bool
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterDelete(
+        AttributeSetRepositoryInterface $subject,
+        bool $result,
+        AttributeSetInterface $attributeSet
+    ) {
+        /** @var Collection $productCollection */
+        $productCollection = $this->collectionFactory->create();
+        $productCollection->addFieldToFilter('attribute_set_id', ['eq' => $attributeSet->getId()]);
+        $productCollection->delete();
+
+        return $result;
+    }
+}
diff --git a/app/code/Magento/Catalog/Setup/UpgradeSchema.php b/app/code/Magento/Catalog/Setup/UpgradeSchema.php
index 616bee43de00e521439cba4f2447c446bb0e7e03..d08108d1fc22bd7ae3b127c57cfdb8b500c2b6a9 100755
--- a/app/code/Magento/Catalog/Setup/UpgradeSchema.php
+++ b/app/code/Magento/Catalog/Setup/UpgradeSchema.php
@@ -21,6 +21,8 @@ class UpgradeSchema implements UpgradeSchemaInterface
     /**
      * {@inheritdoc}
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
      */
     public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
     {
@@ -126,6 +128,10 @@ class UpgradeSchema implements UpgradeSchemaInterface
             $this->fixCustomerGroupIdColumn($setup);
         }
 
+        if (version_compare($context->getVersion(), '2.2.4', '<')) {
+            $this->removeAttributeSetRelation($setup);
+        }
+
         $setup->endSetup();
     }
 
@@ -699,4 +705,20 @@ class UpgradeSchema implements UpgradeSchemaInterface
         );
         $setup->getConnection()->query($sql);
     }
+
+    /**
+     * Remove foreign key between catalog_product_entity and eav_attribute_set tables.
+     * Drop foreign key to delegate cascade on delete to plugin.
+     * @see \Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts
+     *
+     * @param SchemaSetupInterface $setup
+     * @return void
+     */
+    private function removeAttributeSetRelation(SchemaSetupInterface $setup)
+    {
+        $setup->getConnection()->dropForeignKey(
+            $setup->getTable('catalog_product_entity'),
+            $setup->getFkName('catalog_product_entity', 'attribute_set_id', 'eav_attribute_set', 'attribute_set_id')
+        );
+    }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
index 1bc5e450ae153e442133bc7d65c3b07cd70828ab..f77a6fec283a54737e4677b388ba9cfa3a73592f 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryRepositoryTest.php
@@ -255,7 +255,8 @@ class CategoryRepositoryTest extends \PHPUnit\Framework\TestCase
      */
     public function testSaveWithValidateCategoryException($error, $expectedException, $expectedExceptionMessage)
     {
-        $this->expectException($expectedException, $expectedExceptionMessage);
+        $this->expectException($expectedException);
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $categoryId = 5;
         $categoryMock = $this->createMock(\Magento\Catalog\Model\Category::class);
         $this->extensibleDataObjectConverterMock
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php
index eb5fdabe533038a71c8bf0be824309c99cf95479..c254557904da15c0770496dfc28b5cc858099be5 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php
@@ -48,7 +48,8 @@ class FullTest extends \PHPUnit\Framework\TestCase
             $tableSwitcherMock
         );
 
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $model->execute();
     }
diff --git a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..712aeba59dffe47dbf58defffcf489519d55e3b6
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Plugin\Model\AttributeSetRepository;
+
+use Magento\Catalog\Model\ResourceModel\Product\Collection;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
+use Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts;
+use Magento\Eav\Api\AttributeSetRepositoryInterface;
+use Magento\Eav\Api\Data\AttributeSetInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Provide tests for RemoveProducts plugin.
+ */
+class RemoveProductsTest extends TestCase
+{
+    /**
+     * @var RemoveProducts
+     */
+    private $testSubject;
+
+    /**
+     * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $collectionFactory;
+
+    /**
+     * @inheritdoc
+     */
+    protected function setUp()
+    {
+        $objectManager = new ObjectManager($this);
+        $this->collectionFactory = $this->getMockBuilder(CollectionFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->testSubject = $objectManager->getObject(
+            RemoveProducts::class,
+            [
+                'collectionFactory' => $this->collectionFactory,
+            ]
+        );
+    }
+
+    /**
+     * Test plugin will delete all related products for given attribute set.
+     */
+    public function testAfterDelete()
+    {
+        $attributeSetId = '1';
+
+        /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collection */
+        $collection = $this->getMockBuilder(Collection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $collection->expects(self::once())
+            ->method('addFieldToFilter')
+            ->with(self::identicalTo('attribute_set_id'), self::identicalTo(['eq' => $attributeSetId]));
+        $collection->expects(self::once())
+            ->method('delete');
+
+        $this->collectionFactory->expects(self::once())
+            ->method('create')
+            ->willReturn($collection);
+
+        /** @var AttributeSetRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $attributeSetRepository */
+        $attributeSetRepository = $this->getMockBuilder(AttributeSetRepositoryInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        /** @var AttributeSetInterface|\PHPUnit_Framework_MockObject_MockObject $attributeSet */
+        $attributeSet = $this->getMockBuilder(AttributeSetInterface::class)
+            ->setMethods(['getId'])
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $attributeSet->expects(self::once())
+            ->method('getId')
+            ->willReturn($attributeSetId);
+
+        self::assertTrue($this->testSubject->afterDelete($attributeSetRepository, true, $attributeSet));
+    }
+}
diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml
index b97e6fc1aa31891365a9eb901e80b456dab0b9a1..34d089580906f035f181e1b34cd85f77dfa59f70 100644
--- a/app/code/Magento/Catalog/etc/adminhtml/di.xml
+++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml
@@ -184,4 +184,7 @@
             <argument name="scopeOverriddenValue" xsi:type="object">Magento\Catalog\Model\Attribute\ScopeOverriddenValue</argument>
         </arguments>
     </type>
+    <type name="Magento\Eav\Api\AttributeSetRepositoryInterface">
+        <plugin name="remove_products" type="Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts"/>
+    </type>
 </config>
diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml
index 18671a32bb4fbc05cd08e5581649de3f11b423aa..26ed173420adb95491694389c71d2a0848bfb198 100644
--- a/app/code/Magento/Catalog/etc/module.xml
+++ b/app/code/Magento/Catalog/etc/module.xml
@@ -6,7 +6,7 @@
  */
 -->
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
-    <module name="Magento_Catalog" setup_version="2.2.3">
+    <module name="Magento_Catalog" setup_version="2.2.4">
         <sequence>
             <module name="Magento_Eav"/>
             <module name="Magento_Cms"/>
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php
index e1a19bf10ecd4a30e1b81464e19aaf7cea42c10d..3590c96bd153260e0c41e9bfcc20cdffd3402712 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php
@@ -44,7 +44,8 @@ class FullTest extends \PHPUnit\Framework\TestCase
             ]
         );
 
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $model->execute();
     }
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
index 4f53f1072e0356d07182bc27c88621207b4e5f76..0b4d5f7ef15f72f54d13cd25c68d1b5ccefa3892 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
@@ -128,7 +128,8 @@ class RegexceptionsTest extends \PHPUnit\Framework\TestCase
 
         $this->object->addColumn($wrongColumnName, $this->cellParameters);
 
-        $this->expectException('\Exception', 'Wrong column name specified.');
+        $this->expectException('\Exception');
+        $this->expectExceptionMessage('Wrong column name specified.');
 
         $this->object->renderCellTemplate($columnName);
     }
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
index 4e65ab3f4cc214c8854a8bf2b39c6d8cf628bece..0f5852c322bdde7c07e71f5862f1cfb267dcbdbb 100644
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
@@ -132,7 +132,8 @@ class ProcessorFacadeTest extends \PHPUnit\Framework\TestCase
      */
     public function testProcessWithValidatorException(LocalizedException $exception)
     {
-        $this->expectException(ValidatorException::class, 'Some error');
+        $this->expectException(ValidatorException::class);
+        $this->expectExceptionMessage('Some error');
         $this->scopeValidatorMock->expects($this->once())
             ->method('isValid')
             ->willThrowException($exception);
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
index c671a5326c4de3aa5f8649ab628c6ee23e413cf3..058f9a380a27da07797a0707a2cbb895398f2bbe 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
@@ -24,7 +24,8 @@ class RelativePathConverterTest extends \PHPUnit\Framework\TestCase
 
         $exceptionMessage = sprintf('Invalid relative path %s in %s node', $relativePath, $nodePath);
 
-        $this->expectException('InvalidArgumentException', $exceptionMessage);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($exceptionMessage);
         $this->_sut->convert($nodePath, $relativePath);
     }
 
@@ -35,7 +36,8 @@ class RelativePathConverterTest extends \PHPUnit\Framework\TestCase
      */
     public function testConvertWithInvalidArguments($nodePath, $relativePath)
     {
-        $this->expectException('InvalidArgumentException', 'Invalid arguments');
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage('Invalid arguments');
         $this->_sut->convert($nodePath, $relativePath);
     }
 
diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
index 2832e8e54e5f66d5409a067518e94900138d4a81..2ddbbd5ffe1e80b4559d25e95f78ce0abff7abfb 100644
--- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
@@ -280,7 +280,8 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
     public function testSetDataByPathWrongDepth($path, $expectedException)
     {
         $expectedException = 'Allowed depth of configuration is 3 (<section>/<group>/<field>). ' . $expectedException;
-        $this->expectException('\UnexpectedValueException', $expectedException);
+        $this->expectException('\UnexpectedValueException');
+        $this->expectExceptionMessage($expectedException);
         $value = 'value';
         $this->_model->setDataByPath($path, $value);
     }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
index 2d824e52c7244f361f84d6d6c146f256fc6bf2ed..ab3fd33322aa52750367a8e471bb8860dfe1a22e 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
@@ -356,7 +356,8 @@ class OptionRepositoryTest extends \PHPUnit\Framework\TestCase
      */
     public function testValidateNewOptionData($attributeId, $label, $optionValues, $msg)
     {
-        $this->expectException(\Magento\Framework\Exception\InputException::class, $msg);
+        $this->expectException(\Magento\Framework\Exception\InputException::class);
+        $this->expectExceptionMessage($msg);
         $optionValueMock = $this->getMockBuilder(\Magento\ConfigurableProduct\Api\Data\OptionValueInterface::class)
             ->setMethods(['getValueIndex', 'getPricingValue', 'getIsPercent'])
             ->getMockForAbstractClass();
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php
index 5b4a8d5b8a9756421581d15383d24818c72562ba..a3f1435f84d2fad4823959803ff195b2b8d2edf4 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php
@@ -32,13 +32,15 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase
 
     public function testGetWithScalar()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument is not an object');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument is not an object');
         $this->model->getTags('scalar');
     }
 
     public function testGetTagsWithObject()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument must be a product');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument must be a product');
         $this->model->getTags(new \stdClass());
     }
 
diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php
index 2d6917efdaf56812618d0750d4bff4dbb09e01f2..6a8472758c16972a82647eba83cb9f616813c352 100644
--- a/app/code/Magento/Customer/Model/FileProcessor.php
+++ b/app/code/Magento/Customer/Model/FileProcessor.php
@@ -202,7 +202,7 @@ class FileProcessor
     {
         $fileName = ltrim($fileName, '/');
 
-        $dispersionPath = \Magento\MediaStorage\Model\File\Uploader::getDispretionPath($fileName);
+        $dispersionPath = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName);
         $destinationPath = $this->entityTypeCode . $dispersionPath;
 
         if (!$this->mediaDirectory->create($destinationPath)) {
diff --git a/app/code/Magento/Customer/Test/Unit/Model/LoggerTest.php b/app/code/Magento/Customer/Test/Unit/Model/LoggerTest.php
index 4cea7ee22837de2b4be4ecbc378897d7cc5fe1f5..408389182ae499ef5ea30f21f74c89db6cd73455 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/LoggerTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/LoggerTest.php
@@ -71,7 +71,8 @@ class LoggerTest extends \PHPUnit\Framework\TestCase
         $data = array_filter($data);
 
         if (!$data) {
-            $this->expectException('\InvalidArgumentException', 'Log data is empty');
+            $this->expectException('\InvalidArgumentException');
+            $this->expectExceptionMessage('Log data is empty');
             $this->logger->log($customerId, $data);
             return;
         }
diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/ConfigTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/ConfigTest.php
index 54ec26386f3bd33c8c2dcddb43771ea8eaad5941..76b23e4e79ec2b97c2d4451cd00e251ba111690e 100644
--- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/ConfigTest.php
+++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/ConfigTest.php
@@ -29,7 +29,8 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
      */
     public function testConstructorException(array $configData, $expectedException)
     {
-        $this->expectException('InvalidArgumentException', $expectedException);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($expectedException);
         new \Magento\Directory\Model\Currency\Import\Config($configData);
     }
 
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/SetTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/SetTest.php
index 78465e57c62367fd2cd801bb69f5f5b147381642..e976d031e965f8e531cbc818af57dbbdc2c5c839 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/SetTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/SetTest.php
@@ -44,7 +44,8 @@ class SetTest extends \PHPUnit\Framework\TestCase
     {
         $this->_model->getResource()->expects($this->any())->method('validate')->will($this->returnValue(false));
 
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->_model->setAttributeSetName($attributeSetName);
         $this->_model->validate();
     }
diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php
index 6ec3905fe46339ada431037cd7de0bf84ce0e9d2..47c3ac1e7e45075a7d8ab6c9ca33fb13193dd355 100644
--- a/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php
+++ b/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php
@@ -311,7 +311,8 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
         array $fixtureFields = [],
         $argument = null
     ) {
-        $this->expectException('UnexpectedValueException', $expectedException);
+        $this->expectException('UnexpectedValueException');
+        $this->expectExceptionMessage($expectedException);
         $dataStorage = $this->createPartialMock(\Magento\Email\Model\Template\Config\Data::class, ['get']);
         $dataStorage->expects(
             $this->atLeastOnce()
diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
index 6590f6e0af99d4b0ce21468483cdae75c1519f93..22acdc6f82bbcdf5f8a6cae2461f23abd23d73c2 100644
--- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
+++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php
@@ -7,6 +7,8 @@ namespace Magento\Indexer\Console\Command;
 
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
+use Magento\Framework\Indexer;
+use Magento\Framework\Mview;
 
 /**
  * Command for displaying status of indexers.
@@ -30,21 +32,84 @@ class IndexerStatusCommand extends AbstractIndexerManageCommand
      */
     protected function execute(InputInterface $input, OutputInterface $output)
     {
+        $table = $this->getHelperSet()->get('table');
+        $table->setHeaders(['Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']);
+
+        $rows = [];
+
         $indexers = $this->getIndexers($input);
         foreach ($indexers as $indexer) {
-            $status = 'unknown';
-            switch ($indexer->getStatus()) {
-                case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
-                    $status = 'Ready';
-                    break;
-                case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
-                    $status = 'Reindex required';
-                    break;
-                case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
-                    $status = 'Processing';
-                    break;
+            $view = $indexer->getView();
+
+            $rowData = [
+                'Title'             => $indexer->getTitle(),
+                'Status'            => $this->getStatus($indexer),
+                'Update On'         => $indexer->isScheduled() ? 'Schedule' : 'Save',
+                'Schedule Status'   => '',
+                'Updated'           => '',
+            ];
+
+            if ($indexer->isScheduled()) {
+                $state = $view->getState();
+                $rowData['Schedule Status'] = "{$state->getStatus()} ({$this->getPendingCount($view)} in backlog)";
+                $rowData['Updated'] = $state->getUpdated();
             }
-            $output->writeln(sprintf('%-50s ', $indexer->getTitle() . ':') . $status);
+
+            $rows[] = $rowData;
+        }
+
+        usort($rows, function ($comp1, $comp2) {
+            return strcmp($comp1['Title'], $comp2['Title']);
+        });
+
+        $table->addRows($rows);
+        $table->render($output);
+    }
+
+    /**
+     * @param Indexer\IndexerInterface $indexer
+     * @return string
+     */
+    private function getStatus(Indexer\IndexerInterface $indexer)
+    {
+        $status = 'unknown';
+        switch ($indexer->getStatus()) {
+            case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
+                $status = 'Ready';
+                break;
+            case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
+                $status = 'Reindex required';
+                break;
+            case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
+                $status = 'Processing';
+                break;
         }
+        return $status;
+    }
+
+    /**
+     * @param Mview\ViewInterface $view
+     * @return string
+     */
+    private function getPendingCount(Mview\ViewInterface $view)
+    {
+        $changelog = $view->getChangelog();
+
+        try {
+            $currentVersionId = $changelog->getVersion();
+        } catch (Mview\View\ChangelogTableNotExistsException $e) {
+            return '';
+        }
+
+        $state = $view->getState();
+
+        $pendingCount = count($changelog->getList($state->getVersionId(), $currentVersionId));
+
+        $pendingString = "<error>$pendingCount</error>";
+        if ($pendingCount <= 0) {
+            $pendingString = "<info>$pendingCount</info>";
+        }
+
+        return $pendingString;
     }
 }
diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
index 6eb7f7562b9cc2752e553bb994a076c89812b9ff..31513da018c6bf487bec96f57bacad1996c09e50 100644
--- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
+++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php
@@ -8,6 +8,8 @@ namespace Magento\Indexer\Test\Unit\Console\Command;
 use Magento\Framework\Indexer\StateInterface;
 use Magento\Indexer\Console\Command\IndexerStatusCommand;
 use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Console\Helper\HelperSet;
+use Symfony\Component\Console\Helper\TableHelper;
 
 class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
 {
@@ -18,35 +20,134 @@ class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
      */
     private $command;
 
+    /**
+     * @param \PHPUnit_Framework_MockObject_MockObject $indexerMock
+     * @param array $data
+     * @return mixed
+     */
+    private function attachViewToIndexerMock($indexerMock, array $data)
+    {
+         /** @var \Magento\Framework\Mview\View\Changelog|\PHPUnit_Framework_MockObject_MockObject $changelog */
+        $changelog = $this->getMockBuilder(\Magento\Framework\Mview\View\Changelog::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $changelog->expects($this->any())
+            ->method('getList')
+            ->willReturn(range(0, $data['view']['changelog']['list_size']-1));
+
+        /** @var \Magento\Indexer\Model\Mview\View\State|\PHPUnit_Framework_MockObject_MockObject $stateMock */
+        $stateMock = $this->getMockBuilder(\Magento\Indexer\Model\Mview\View\State::class)
+            ->disableOriginalConstructor()
+            ->setMethods(null)
+            ->getMock();
+
+        $stateMock->addData($data['view']['state']);
+
+        /** @var \Magento\Framework\Mview\View|\PHPUnit_Framework_MockObject_MockObject $viewMock */
+        $viewMock = $this->getMockBuilder(\Magento\Framework\Mview\View::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getChangelog', 'getState'])
+            ->getMock();
+
+        $viewMock->expects($this->any())
+            ->method('getState')
+            ->willReturn($stateMock);
+        $viewMock->expects($this->any())
+            ->method('getChangelog')
+            ->willReturn($changelog);
+
+        $indexerMock->method('getView')
+            ->willReturn($viewMock);
+
+        return $indexerMock;
+    }
+
     /**
      * @param array $indexers
-     * @param array $statuses
+     *
      * @dataProvider executeAllDataProvider
      */
-    public function testExecuteAll(array $indexers, array $statuses)
+    public function testExecuteAll(array $indexers)
     {
         $this->configureAdminArea();
         $indexerMocks = [];
         foreach ($indexers as $indexerData) {
             $indexerMock = $this->getIndexerMock(
-                ['getStatus'],
+                ['getStatus', 'isScheduled', 'getState', 'getView'],
                 $indexerData
             );
+
             $indexerMock->method('getStatus')
-                ->willReturn($statuses[$indexerData['indexer_id']]);
+                ->willReturn($indexerData['status']);
+            $indexerMock->method('isScheduled')
+                ->willReturn($indexerData['is_scheduled']);
+
+            if ($indexerData['is_scheduled']) {
+                $this->attachViewToIndexerMock($indexerMock, $indexerData);
+            }
+
             $indexerMocks[] = $indexerMock;
         }
+
         $this->initIndexerCollectionByItems($indexerMocks);
         $this->command = new IndexerStatusCommand($this->objectManagerFactory);
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->command->setHelperSet(
+            $objectManager->getObject(
+                HelperSet::class,
+                ['helpers' => [$objectManager->getObject(TableHelper::class)]]
+            )
+        );
+        
         $commandTester = new CommandTester($this->command);
         $commandTester->execute([]);
-        $actualValue = $commandTester->getDisplay();
-        $expectedValue = sprintf('%-50s ', 'Title_indexerOne' . ':') . 'Ready' . PHP_EOL
-            . sprintf('%-50s ', 'Title_indexerTwo' . ':') . 'Reindex required' . PHP_EOL
-            . sprintf('%-50s ', 'Title_indexerThree' . ':') . 'Processing' . PHP_EOL
-            . sprintf('%-50s ', 'Title_indexerFour' . ':') . 'unknown' . PHP_EOL;
 
-        $this->assertStringStartsWith($expectedValue, $actualValue);
+        $linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay()));
+
+        $spacer = '+----------------+------------------+-----------+-------------------------+---------------------+';
+
+        $this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.');
+        $this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines");
+        $this->assertEquals($linesOutput[2], $spacer, "Lines 0, 2, 7 should be spacer lines");
+        $this->assertEquals($linesOutput[7], $spacer, "Lines 0, 2, 7 should be spacer lines");
+
+        $headerValues = array_values(array_filter(explode('|', $linesOutput[1])));
+        $this->assertEquals('Title', trim($headerValues[0]));
+        $this->assertEquals('Status', trim($headerValues[1]));
+        $this->assertEquals('Update On', trim($headerValues[2]));
+        $this->assertEquals('Schedule Status', trim($headerValues[3]));
+        $this->assertEquals('Schedule Updated', trim($headerValues[4]));
+
+        $indexer1 = array_values(array_filter(explode('|', $linesOutput[3])));
+        $this->assertEquals('Title_indexer1', trim($indexer1[0]));
+        $this->assertEquals('Ready', trim($indexer1[1]));
+        $this->assertEquals('Schedule', trim($indexer1[2]));
+        $this->assertEquals('idle (10 in backlog)', trim($indexer1[3]));
+        $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[4]));
+
+        $indexer2 = array_values(array_filter(explode('|', $linesOutput[4])));
+        $this->assertEquals('Title_indexer2', trim($indexer2[0]));
+        $this->assertEquals('Reindex required', trim($indexer2[1]));
+        $this->assertEquals('Save', trim($indexer2[2]));
+        $this->assertEquals('', trim($indexer2[3]));
+        $this->assertEquals('', trim($indexer2[4]));
+
+        $indexer3 = array_values(array_filter(explode('|', $linesOutput[5])));
+        $this->assertEquals('Title_indexer3', trim($indexer3[0]));
+        $this->assertEquals('Processing', trim($indexer3[1]));
+        $this->assertEquals('Schedule', trim($indexer3[2]));
+        $this->assertEquals('idle (100 in backlog)', trim($indexer3[3]));
+        $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[4]));
+
+        $indexer4 = array_values(array_filter(explode('|', $linesOutput[6])));
+        $this->assertEquals('Title_indexer4', trim($indexer4[0]));
+        $this->assertEquals('unknown', trim($indexer4[1]));
+        $this->assertEquals('Schedule', trim($indexer4[2]));
+        $this->assertEquals('running (20 in backlog)', trim($indexer4[3]));
+        $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[4]));
     }
 
     /**
@@ -59,27 +160,65 @@ class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
                 'indexers' => [
                     'indexer_1' => [
                         'indexer_id' => 'indexer_1',
-                        'title' => 'Title_indexerOne'
+                        'title' => 'Title_indexer1',
+                        'status' => StateInterface::STATUS_VALID,
+                        'is_scheduled' => true,
+                        'view' => [
+                            'state' => [
+                                'status' => 'idle',
+                                'updated' => '2017-01-01 11:11:11',
+                            ],
+                            'changelog' => [
+                                'list_size' => 10
+                            ]
+                        ]
                     ],
                     'indexer_2' => [
                         'indexer_id' => 'indexer_2',
-                        'title' => 'Title_indexerTwo'
+                        'title' => 'Title_indexer2',
+                        'status' => StateInterface::STATUS_INVALID,
+                        'is_scheduled' => false,
+                        'view' => [
+                            'state' => [
+                                'status' => 'idle',
+                                'updated' => '2017-01-01 11:11:11',
+                            ],
+                            'changelog' => [
+                                'list_size' => 99999999
+                            ]
+                        ]
                     ],
                     'indexer_3' => [
                         'indexer_id' => 'indexer_3',
-                        'title' => 'Title_indexerThree'
+                        'title' => 'Title_indexer3',
+                        'status' => StateInterface::STATUS_WORKING,
+                        'is_scheduled' => true,
+                        'view' => [
+                            'state' => [
+                                'status' => 'idle',
+                                'updated' => '2017-01-01 11:11:11',
+                            ],
+                            'changelog' => [
+                                'list_size' => 100
+                            ]
+                        ]
                     ],
                     'indexer_4' => [
                         'indexer_id' => 'indexer_4',
-                        'title' => 'Title_indexerFour'
+                        'title' => 'Title_indexer4',
+                        'status' => null,
+                        'is_scheduled' => true,
+                        'view' => [
+                            'state' => [
+                                'status' => 'running',
+                                'updated' => '2017-01-01 11:11:11',
+                            ],
+                            'changelog' => [
+                                'list_size' => 20
+                            ]
+                        ]
                     ],
                 ],
-                'Statuses' => [
-                    'indexer_1' => StateInterface::STATUS_VALID,
-                    'indexer_2' => StateInterface::STATUS_INVALID,
-                    'indexer_3' => StateInterface::STATUS_WORKING,
-                    'indexer_4' => null,
-                ]
             ],
         ];
     }
diff --git a/app/code/Magento/Integration/Test/Unit/Model/Oauth/TokenTest.php b/app/code/Magento/Integration/Test/Unit/Model/Oauth/TokenTest.php
index 36b0d77d1e1aef340c1df86ec6430911fdc51325..badb69aa19fe4ac63f184a8bcead73d5f7f738da 100644
--- a/app/code/Magento/Integration/Test/Unit/Model/Oauth/TokenTest.php
+++ b/app/code/Magento/Integration/Test/Unit/Model/Oauth/TokenTest.php
@@ -384,7 +384,8 @@ class TokenTest extends \PHPUnit\Framework\TestCase
         $this->validatorMock->expects($this->once())->method('isValid')->willReturn(false);
         $this->validatorMock->expects($this->once())->method('getMessages')->willReturn([$exceptionMessage]);
 
-        $this->expectException(\Magento\Framework\Oauth\Exception::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Oauth\Exception::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $this->tokenModel->validate();
     }
@@ -402,7 +403,8 @@ class TokenTest extends \PHPUnit\Framework\TestCase
         $this->validatorKeyLengthMock->expects($this->once())->method('isValid')->willReturn(false);
         $this->validatorKeyLengthMock->expects($this->once())->method('getMessages')->willReturn([$exceptionMessage]);
 
-        $this->expectException(\Magento\Framework\Oauth\Exception::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Oauth\Exception::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $this->tokenModel->validate();
     }
@@ -429,7 +431,8 @@ class TokenTest extends \PHPUnit\Framework\TestCase
             ]
         );
         $this->validatorKeyLengthMock->expects($this->once())->method('getMessages')->willReturn([$exceptionMessage]);
-        $this->expectException(\Magento\Framework\Oauth\Exception::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Oauth\Exception::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $this->tokenModel->validate();
     }
@@ -459,7 +462,8 @@ class TokenTest extends \PHPUnit\Framework\TestCase
             ]
         );
         $this->validatorKeyLengthMock->expects($this->once())->method('getMessages')->willReturn([$exceptionMessage]);
-        $this->expectException(\Magento\Framework\Oauth\Exception::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Oauth\Exception::class);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $this->tokenModel->validate();
     }
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php
index 8b270fe24ab155778f9fd5c46abdbe7bff509d3d..c0b2bb4fc1dca4a4aae440791d8c0bc105fad3a9 100644
--- a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php
@@ -126,7 +126,8 @@ class NvpTest extends \PHPUnit\Framework\TestCase
     public function testCall($response, $processableErrors, $exception, $exceptionMessage = '', $exceptionCode = null)
     {
         if (isset($exception)) {
-            $this->expectException($exception, $exceptionMessage, $exceptionCode);
+            $this->expectException($exception);
+            $this->expectExceptionMessage($exceptionMessage, $exceptionCode);
         }
         $this->curl->expects($this->once())
             ->method('read')
diff --git a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php
index 3658e36a82ec316460aa4b15731172e824d14454..9950526182e3e16a23e16a05fb2188bf2bc44ec0 100644
--- a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php
+++ b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php
@@ -110,7 +110,7 @@ class RetrieveImage extends \Magento\Backend\App\Action
             $remoteFileUrl = $this->getRequest()->getParam('remote_image');
             $this->validateRemoteFile($remoteFileUrl);
             $localFileName = Uploader::getCorrectFileName(basename($remoteFileUrl));
-            $localTmpFileName = Uploader::getDispretionPath($localFileName) . DIRECTORY_SEPARATOR . $localFileName;
+            $localTmpFileName = Uploader::getDispersionPath($localFileName) . DIRECTORY_SEPARATOR . $localFileName;
             $localFilePath = $baseTmpMediaPath . ($localTmpFileName);
             $localUniqFilePath = $this->appendNewFileName($localFilePath);
             $this->validateRemoteFileExtensions($localUniqFilePath);
@@ -174,7 +174,7 @@ class RetrieveImage extends \Magento\Backend\App\Action
     protected function appendResultSaveRemoteImage($fileName)
     {
         $fileInfo = pathinfo($fileName);
-        $tmpFileName = Uploader::getDispretionPath($fileInfo['basename']) . DIRECTORY_SEPARATOR . $fileInfo['basename'];
+        $tmpFileName = Uploader::getDispersionPath($fileInfo['basename']) . DIRECTORY_SEPARATOR . $fileInfo['basename'];
         $result['name'] = $fileInfo['basename'];
         $result['type'] = $this->imageAdapter->getMimeType();
         $result['error'] = 0;
diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
index 1dfcc95a552c69b7ffed247ec83c348a8f80d4df..c0036b71ac86ac7550b0faaa6d59f0382141cb2e 100644
--- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
+++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js
@@ -175,10 +175,10 @@ define([
          */
         clearEvents: function () {
             this.fotoramaItem.off(
-                'fotorama:show ' +
-                'fotorama:showend ' +
-                'fotorama:fullscreenenter ' +
-                'fotorama:fullscreenexit'
+                'fotorama:show.' + this.PV +
+                ' fotorama:showend.' + this.PV +
+                ' fotorama:fullscreenenter.' + this.PV +
+                ' fotorama:fullscreenexit.' + this.PV
             );
         },
 
@@ -232,11 +232,11 @@ define([
          * @private
          */
         _listenForFullscreen: function () {
-            this.fotoramaItem.on('fotorama:fullscreenenter', $.proxy(function () {
+            this.fotoramaItem.on('fotorama:fullscreenenter.' + this.PV, $.proxy(function () {
                 this.isFullscreen = true;
             }, this));
 
-            this.fotoramaItem.on('fotorama:fullscreenexit', $.proxy(function () {
+            this.fotoramaItem.on('fotorama:fullscreenexit.' + this.PV, $.proxy(function () {
                 this.isFullscreen = false;
                 this._hideVideoArrows();
             }, this));
@@ -468,7 +468,7 @@ define([
                 t;
 
             if (!fotorama.activeFrame.$navThumbFrame) {
-                this.fotoramaItem.on('fotorama:showend', $.proxy(function (evt, fotoramaData) {
+                this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (evt, fotoramaData) {
                     $(fotoramaData.activeFrame.$stageFrame).removeAttr('href');
                 }, this));
 
@@ -486,7 +486,7 @@ define([
                 this._checkForVideo(e, fotorama, t + 1);
             }
 
-            this.fotoramaItem.on('fotorama:showend', $.proxy(function (evt, fotoramaData) {
+            this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (evt, fotoramaData) {
                 $(fotoramaData.activeFrame.$stageFrame).removeAttr('href');
             }, this));
         },
@@ -528,15 +528,15 @@ define([
          * @private
          */
         _attachFotoramaEvents: function () {
-            this.fotoramaItem.on('fotorama:showend', $.proxy(function (e, fotorama) {
+            this.fotoramaItem.on('fotorama:showend.' + this.PV, $.proxy(function (e, fotorama) {
                 this._startPrepareForPlayer(e, fotorama);
             }, this));
 
-            this.fotoramaItem.on('fotorama:show', $.proxy(function (e, fotorama) {
+            this.fotoramaItem.on('fotorama:show.' + this.PV, $.proxy(function (e, fotorama) {
                 this._unloadVideoPlayer(fotorama.activeFrame.$stageFrame.parent(), fotorama, true);
             }, this));
 
-            this.fotoramaItem.on('fotorama:fullscreenexit', $.proxy(function (e, fotorama) {
+            this.fotoramaItem.on('fotorama:fullscreenexit.' + this.PV, $.proxy(function (e, fotorama) {
                 fotorama.activeFrame.$stageFrame.find('.' + this.PV).remove();
                 this._startPrepareForPlayer(e, fotorama);
             }, this));
diff --git a/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php
index f3f14ef8c35432b2907cdeaf96442b6b728dbe96..d219aefe81d458633b446f1768855fd433ec4e71 100644
--- a/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php
+++ b/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php
@@ -220,8 +220,10 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
         $orderData = $this->getOrdersData($productIds);
         foreach ($items as $item) {
             $item->setId($item->getProductId());
-            $item->setPrice($productData[$item->getProductId()]['price'] * $item->getBaseToGlobalRate());
-            $item->setName($productData[$item->getProductId()]['name']);
+            if (isset($productData[$item->getProductId()])) {
+                $item->setPrice($productData[$item->getProductId()]['price'] * $item->getBaseToGlobalRate());
+                $item->setName($productData[$item->getProductId()]['name']);
+            }
             $item->setOrders(0);
             if (isset($orderData[$item->getProductId()])) {
                 $item->setOrders($orderData[$item->getProductId()]['orders']);
diff --git a/app/code/Magento/Rule/Test/Unit/Model/ConditionFactoryTest.php b/app/code/Magento/Rule/Test/Unit/Model/ConditionFactoryTest.php
index d8c0cc470f55e96e5977c4b8e16572c904d9a9dd..f78ee4f345d0de4adac4b21255964a7bcaca4389 100644
--- a/app/code/Magento/Rule/Test/Unit/Model/ConditionFactoryTest.php
+++ b/app/code/Magento/Rule/Test/Unit/Model/ConditionFactoryTest.php
@@ -78,7 +78,8 @@ class ConditionFactoryTest extends \PHPUnit\Framework\TestCase
             ->expects($this->never())
             ->method('create');
 
-        $this->expectException(\InvalidArgumentException::class, 'Class does not exist');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Class does not exist');
 
         $this->conditionFactory->create($type);
     }
@@ -92,7 +93,8 @@ class ConditionFactoryTest extends \PHPUnit\Framework\TestCase
             ->method('create')
             ->with($type)
             ->willReturn(new \stdClass());
-        $this->expectException(\InvalidArgumentException::class, 'Class does not implement condition interface');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Class does not implement condition interface');
         $this->conditionFactory->create($type);
     }
 }
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/CouponRepositoryTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/CouponRepositoryTest.php
index ebdc10830f33f20fdcfc4a9766a1184fa565b0b6..31536e1be3d2eb92b111ea0c8c8855e7bbf91932 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/CouponRepositoryTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/CouponRepositoryTest.php
@@ -150,7 +150,8 @@ class CouponRepositoryTest extends \PHPUnit\Framework\TestCase
             $this->resource->expects($this->once())->method('save')->with($coupon)
                 ->willThrowException($exceptionObject);
         }
-        $this->expectException($exceptionName, $exceptionMessage);
+        $this->expectException($exceptionName);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->model->save($coupon);
     }
 
diff --git a/app/code/Magento/Security/Model/Config.php b/app/code/Magento/Security/Model/Config.php
index 100f4630a45a654891794a1d8a34914513b9c550..2135b81eb82b55b92a10f77a429af3ede1307699 100644
--- a/app/code/Magento/Security/Model/Config.php
+++ b/app/code/Magento/Security/Model/Config.php
@@ -24,10 +24,17 @@ class Config implements ConfigInterface
      */
     const XML_PATH_ADMIN_AREA = 'admin/security/';
 
+    /**
+     * Configuration path to frontend area
+     */
+    const XML_PATH_FRONTEND_AREA = 'customer/password/';
+
     /**
      * Configuration path to fronted area
+     * @deprecated
+     * @see \Magento\Security\Model\Config::XML_PATH_FRONTEND_AREA
      */
-    const XML_PATH_FRONTED_AREA = 'customer/password/';
+    const XML_PATH_FRONTED_AREA = self::XML_PATH_FRONTEND_AREA;
 
     /**
      * Configuration path to admin account sharing
@@ -134,7 +141,7 @@ class Config implements ConfigInterface
         if ($this->scope->getCurrentScope() == \Magento\Framework\App\Area::AREA_ADMINHTML) {
             return self::XML_PATH_ADMIN_AREA;
         }
-        return self::XML_PATH_FRONTED_AREA;
+        return self::XML_PATH_FRONTEND_AREA;
     }
 
     /**
diff --git a/app/code/Magento/Security/Model/Plugin/AccountManagement.php b/app/code/Magento/Security/Model/Plugin/AccountManagement.php
index dea54b194880d7629c1d8f3f6dec2a95265ed117..9476bf46df338506cd56c8316fc4915b9d60f64c 100644
--- a/app/code/Magento/Security/Model/Plugin/AccountManagement.php
+++ b/app/code/Magento/Security/Model/Plugin/AccountManagement.php
@@ -5,10 +5,12 @@
  */
 namespace Magento\Security\Model\Plugin;
 
-use Magento\Security\Model\SecurityManager;
 use Magento\Customer\Model\AccountManagement as AccountManagementOriginal;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Config\ScopeInterface;
 use Magento\Framework\Exception\SecurityViolationException;
 use Magento\Security\Model\PasswordResetRequestEvent;
+use Magento\Security\Model\SecurityManager;
 
 /**
  * Magento\Customer\Model\AccountManagement decorator
@@ -30,21 +32,29 @@ class AccountManagement
      */
     protected $passwordRequestEvent;
 
+    /**
+     * @var ScopeInterface
+     */
+    private $scope;
+
     /**
      * AccountManagement constructor.
      *
      * @param \Magento\Framework\App\RequestInterface $request
      * @param SecurityManager $securityManager
      * @param int $passwordRequestEvent
+     * @param ScopeInterface $scope
      */
     public function __construct(
         \Magento\Framework\App\RequestInterface $request,
         \Magento\Security\Model\SecurityManager $securityManager,
-        $passwordRequestEvent = PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+        $passwordRequestEvent = PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST,
+        ScopeInterface $scope = null
     ) {
         $this->request = $request;
         $this->securityManager = $securityManager;
         $this->passwordRequestEvent = $passwordRequestEvent;
+        $this->scope = $scope ?: ObjectManager::getInstance()->get(ScopeInterface::class);
     }
 
     /**
@@ -63,10 +73,14 @@ class AccountManagement
         $template,
         $websiteId = null
     ) {
-        $this->securityManager->performSecurityCheck(
-            $this->passwordRequestEvent,
-            $email
-        );
+        if ($this->scope->getCurrentScope() == \Magento\Framework\App\Area::AREA_FRONTEND
+            || $this->passwordRequestEvent == PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST) {
+            $this->securityManager->performSecurityCheck(
+                $this->passwordRequestEvent,
+                $email
+            );
+        }
+
         return [$email, $template, $websiteId];
     }
 }
diff --git a/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
index 7186502df73b5d6632b8e3ef7a2e92fde084be31..3ef8655539b5a1f679b0924d72e9f72857d50db5 100644
--- a/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
@@ -167,7 +167,7 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
         if ($scope == \Magento\Framework\App\Area::AREA_ADMINHTML) {
             return \Magento\Security\Model\Config::XML_PATH_ADMIN_AREA;
         }
-        return \Magento\Security\Model\Config::XML_PATH_FRONTED_AREA;
+        return \Magento\Security\Model\Config::XML_PATH_FRONTEND_AREA;
     }
 
     /**
diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
index 0935dc003d5b3c9a8a03fa6e70c4750c37c79b06..8f8128d395a0c00d04fb11d38248bbdd010b1c22 100644
--- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
@@ -6,7 +6,11 @@
 
 namespace Magento\Security\Test\Unit\Model\Plugin;
 
+use Magento\Customer\Model\AccountManagement;
+use Magento\Framework\App\Area;
+use Magento\Framework\Config\ScopeInterface;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Security\Model\PasswordResetRequestEvent;
 
 /**
  * Test class for \Magento\Security\Model\Plugin\AccountManagement testing
@@ -19,20 +23,25 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase
     protected $model;
 
     /**
-     * @var \Magento\Framework\App\RequestInterface
+     * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $request;
 
     /**
-     * @var \Magento\Security\Model\SecurityManager
+     * @var \Magento\Security\Model\SecurityManager|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $securityManager;
 
     /**
-     * @var \Magento\Customer\Model\AccountManagement
+     * @var AccountManagement|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $accountManagement;
 
+    /**
+     * @var ScopeInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $scope;
+
     /**
      * @var  \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
      */
@@ -46,35 +55,45 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase
     {
         $this->objectManager = new ObjectManager($this);
 
-        $this->request =  $this->createMock(\Magento\Framework\App\RequestInterface::class);
+        $this->request = $this->createMock(\Magento\Framework\App\RequestInterface::class);
 
         $this->securityManager = $this->createPartialMock(
             \Magento\Security\Model\SecurityManager::class,
             ['performSecurityCheck']
         );
 
-        $this->accountManagement =  $this->createMock(\Magento\Customer\Model\AccountManagement::class);
+        $this->accountManagement = $this->createMock(AccountManagement::class);
+        $this->scope = $this->createMock(ScopeInterface::class);
+    }
+
+    /**
+     * @param $area
+     * @param $passwordRequestEvent
+     * @param $expectedTimes
+     * @dataProvider beforeInitiatePasswordResetDataProvider
+     */
+    public function testBeforeInitiatePasswordReset($area, $passwordRequestEvent, $expectedTimes)
+    {
+        $email = 'test@example.com';
+        $template = AccountManagement::EMAIL_RESET;
 
         $this->model = $this->objectManager->getObject(
             \Magento\Security\Model\Plugin\AccountManagement::class,
             [
+                'passwordRequestEvent' => $passwordRequestEvent,
                 'request' => $this->request,
-                'securityManager' => $this->securityManager
+                'securityManager' => $this->securityManager,
+                'scope' => $this->scope
             ]
         );
-    }
 
-    /**
-     * @return void
-     */
-    public function testBeforeInitiatePasswordReset()
-    {
-        $email = 'test@example.com';
-        $template = \Magento\Customer\Model\AccountManagement::EMAIL_RESET;
+        $this->scope->expects($this->once())
+            ->method('getCurrentScope')
+            ->willReturn($area);
 
-        $this->securityManager->expects($this->once())
+        $this->securityManager->expects($this->exactly($expectedTimes))
             ->method('performSecurityCheck')
-            ->with(\Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, $email)
+            ->with($passwordRequestEvent, $email)
             ->willReturnSelf();
 
         $this->model->beforeInitiatePasswordReset(
@@ -83,4 +102,18 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase
             $template
         );
     }
+
+    /**
+     * @return array
+     */
+    public function beforeInitiatePasswordResetDataProvider()
+    {
+        return [
+            [Area::AREA_ADMINHTML, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 0],
+            [Area::AREA_ADMINHTML, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
+            [Area::AREA_FRONTEND, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 1],
+            // This should never happen, but let's cover it with tests
+            [Area::AREA_FRONTEND, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
+        ];
+    }
 }
diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml
index 6f07fb580490e06e684ab5b8644cc995e2753357..c1188c2d405cf99b018be6131b7280c7e7784c3f 100644
--- a/app/code/Magento/Security/etc/adminhtml/di.xml
+++ b/app/code/Magento/Security/etc/adminhtml/di.xml
@@ -17,7 +17,7 @@
     </type>
     <type name="Magento\Security\Model\Plugin\AccountManagement">
         <arguments>
-            <argument name="passwordRequestEvent" xsi:type="const">Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST</argument>
+            <argument name="passwordRequestEvent" xsi:type="const">Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST</argument>
         </arguments>
     </type>
     <type name="Magento\Security\Model\SecurityManager">
diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php
index db2053efd41f616002de8c78b4663f90a6e887b4..e6a29177301dd2fbabb741ac6e6ba10d4b545ffc 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php
@@ -307,7 +307,8 @@ class RateRepositoryTest extends \PHPUnit\Framework\TestCase
             ->with($rateTitles)
             ->willThrowException($expectedException);
         $this->rateRegistryMock->expects($this->never())->method('registerTaxRate')->with($rateMock);
-        $this->expectException($exceptionType, $exceptionMessage);
+        $this->expectException($exceptionType);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->model->save($rateMock);
     }
 
diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateTest.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateTest.php
index 5a5abfd828d889f7e00fd122bd5d7d70825411c3..c0a04a3fb45f64e84fbf8468936a2f553851ae46 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateTest.php
@@ -46,7 +46,8 @@ class RateTest extends \PHPUnit\Framework\TestCase
      */
     public function testExceptionOfValidation($exceptionMessage, $data)
     {
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $exceptionMessage);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($exceptionMessage);
         $rate = $this->objectHelper->getObject(
             \Magento\Tax\Model\Calculation\Rate::class,
             ['resource' => $this->resourceMock]
diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxRuleRepositoryTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxRuleRepositoryTest.php
index 182e1b43d786c3f0763f438566a0f4efc067ee29..f4151cd18ba665d365414621db3b800f6dd7a9f9 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/TaxRuleRepositoryTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/TaxRuleRepositoryTest.php
@@ -163,7 +163,8 @@ class TaxRuleRepositoryTest extends \PHPUnit\Framework\TestCase
             ->willThrowException($exceptionObject);
         $this->taxRuleRegistry->expects($this->never())->method('registerTaxRule');
 
-        $this->expectException($exceptionName, $exceptionMessage);
+        $this->expectException($exceptionName);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->model->save($rule);
     }
 
diff --git a/app/code/Magento/Theme/Test/Unit/Observer/CleanThemeRelatedContentObserverTest.php b/app/code/Magento/Theme/Test/Unit/Observer/CleanThemeRelatedContentObserverTest.php
index 0eaa50968561644cfdddd91d685e266f88197272..f1f4664c8541d05550e428df8dd3d64426c31dad 100644
--- a/app/code/Magento/Theme/Test/Unit/Observer/CleanThemeRelatedContentObserverTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Observer/CleanThemeRelatedContentObserverTest.php
@@ -105,7 +105,8 @@ class CleanThemeRelatedContentObserverTest extends \PHPUnit\Framework\TestCase
 
         $this->themeConfig->expects($this->any())->method('isThemeAssignedToStore')->with($themeMock)->willReturn(true);
 
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, 'Theme isn\'t deletable.');
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage('Theme isn\'t deletable.');
         $this->themeObserver->execute($observerMock);
     }
 
diff --git a/app/code/Magento/Ui/Test/Unit/Model/ResourceModel/BookmarkRepositoryTest.php b/app/code/Magento/Ui/Test/Unit/Model/ResourceModel/BookmarkRepositoryTest.php
index 00a88437c8cb1e2e3de147cbca38127e92c5c78c..a0cec2258d658ec12d05dd0160187bea9bc92488 100644
--- a/app/code/Magento/Ui/Test/Unit/Model/ResourceModel/BookmarkRepositoryTest.php
+++ b/app/code/Magento/Ui/Test/Unit/Model/ResourceModel/BookmarkRepositoryTest.php
@@ -94,7 +94,8 @@ class BookmarkRepositoryTest extends \PHPUnit\Framework\TestCase
             ->method('save')
             ->with($this->bookmarkMock)
             ->willThrowException(new \Exception($exceptionMessage));
-        $this->expectException(\Magento\Framework\Exception\CouldNotSaveException::class, __($exceptionMessage));
+        $this->expectException(\Magento\Framework\Exception\CouldNotSaveException::class);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->bookmarkRepository->save($this->bookmarkMock);
     }
 
@@ -143,7 +144,8 @@ class BookmarkRepositoryTest extends \PHPUnit\Framework\TestCase
             ->method('delete')
             ->with($this->bookmarkMock)
             ->willThrowException(new \Exception($exceptionMessage));
-        $this->expectException(\Magento\Framework\Exception\CouldNotDeleteException::class, __($exceptionMessage));
+        $this->expectException(\Magento\Framework\Exception\CouldNotDeleteException::class);
+        $this->expectExceptionMessage($exceptionMessage);
         $this->assertTrue($this->bookmarkRepository->delete($this->bookmarkMock));
     }
 
diff --git a/app/code/Magento/Webapi/Controller/Rest.php b/app/code/Magento/Webapi/Controller/Rest.php
index 1f8260c93c57485e97a384a6fc0ab08fd8d69f24..dc061aeea99e7bf50e2fa2eb2aff16909d0ac139 100644
--- a/app/code/Magento/Webapi/Controller/Rest.php
+++ b/app/code/Magento/Webapi/Controller/Rest.php
@@ -303,7 +303,7 @@ class Rest implements \Magento\Framework\App\FrontControllerInterface
         $responseBody = $this->swaggerGenerator->generate(
             $requestedServices,
             $this->_request->getScheme(),
-            $this->_request->getHttpHost(),
+            $this->_request->getHttpHost(false),
             $this->_request->getRequestUri()
         );
         $this->_response->setBody($responseBody)->setHeader('Content-Type', 'application/json');
diff --git a/app/code/Magento/Webapi/Test/Unit/ExceptionTest.php b/app/code/Magento/Webapi/Test/Unit/ExceptionTest.php
index f4ba35194725d3071e4bfd26c01a6c2175f47651..c3761c4e24862ce985834866199b1e9f49bb277d 100644
--- a/app/code/Magento/Webapi/Test/Unit/ExceptionTest.php
+++ b/app/code/Magento/Webapi/Test/Unit/ExceptionTest.php
@@ -43,7 +43,8 @@ class ExceptionTest extends \PHPUnit\Framework\TestCase
      */
     public function testConstructInvalidHttpCode($httpCode)
     {
-        $this->expectException('InvalidArgumentException', "The specified HTTP code \"{$httpCode}\" is invalid.");
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage("The specified HTTP code \"{$httpCode}\" is invalid.");
         /** Create \Magento\Framework\Webapi\Exception object with invalid code. */
         /** Valid codes range is from 400 to 599. */
         new \Magento\Framework\Webapi\Exception(__('Message'), 0, $httpCode);
diff --git a/app/code/Magento/Weee/Test/Unit/Model/Attribute/Backend/Weee/TaxTest.php b/app/code/Magento/Weee/Test/Unit/Model/Attribute/Backend/Weee/TaxTest.php
index 9e220091d6c2e568f23f7cdf3e3b0466f22d8762..8ba8afaa1a8ab79e6f305a8998061d70fe3996c7 100644
--- a/app/code/Magento/Weee/Test/Unit/Model/Attribute/Backend/Weee/TaxTest.php
+++ b/app/code/Magento/Weee/Test/Unit/Model/Attribute/Backend/Weee/TaxTest.php
@@ -82,7 +82,8 @@ class TaxTest extends \PHPUnit\Framework\TestCase
             ->will($this->returnValue($taxes));
 
         // Exception caught
-        $this->expectException('Exception', $expected);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($expected);
         $modelMock->validate($productMock);
     }
 
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php
index 95e65a1740b72c0c818715c051ac39a0af3e8c0d..0b1057683de8615039edb311afffdeaf5aad29e9 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/ItemTest.php
@@ -299,7 +299,8 @@ class ItemTest extends \PHPUnit\Framework\TestCase
 
     public function testGetProductWithException()
     {
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, __('Cannot specify product.'));
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage('Cannot specify product.');
         $this->model->getProduct();
     }
 
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
index ca9ad9897bf8ca7850024cd58e4419f7ed2ce371..17bc1226d992ca2d172252934845e52836b13e0a 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
@@ -609,9 +609,11 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF
             'sku' => $productSku,
         ];
         if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
-            $this->expectException('SoapFault', 'Requested product doesn\'t exist');
+            $this->expectException('SoapFault');
+            $this->expectExceptionMessage('Requested product doesn\'t exist');
         } else {
-            $this->expectException('Exception', '', 404);
+            $this->expectException('Exception');
+            $this->expectExceptionCode(404);
         }
         $this->_webApiCall($serviceInfo, $requestData);
     }
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomAttributeWrongTypeTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomAttributeWrongTypeTest.php
index 2dc8d197778989986fef6d598251e73862222ce2..19b0757439077907cf3f18bb62c68ac3b4165569 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomAttributeWrongTypeTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomAttributeWrongTypeTest.php
@@ -43,9 +43,11 @@ class ProductCustomAttributeWrongTypeTest extends WebapiAbstract
         ];
 
         if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
-            $this->expectException('Exception', 'Attribute "meta_title" has invalid value.');
+            $this->expectException('Exception');
+            $this->expectExceptionMessage('Attribute "meta_title" has invalid value.');
         } else {
-            $this->expectException('Exception', 'Attribute \"meta_title\" has invalid value.');
+            $this->expectException('Exception');
+            $this->expectExceptionMessage('Attribute \"meta_title\" has invalid value.');
         }
 
         $this->_webApiCall($serviceInfo, $this->getRequestData());
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
index a152167a345fa7cc5b2ad1c3e0c65ce7691e699d..34588c9573f984ebd58ccd2e25581419b55c598e 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
@@ -211,12 +211,15 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract
 
         if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
             if (isset($optionDataPost['title']) && empty($optionDataPost['title'])) {
-                $this->expectException('SoapFault', 'Missed values for option required fields');
+                $this->expectException('SoapFault');
+                $this->expectExceptionMessage('Missed values for option required fields');
             } else {
-                $this->expectException('SoapFault', 'Invalid option');
+                $this->expectException('SoapFault');
+                $this->expectExceptionMessage('Invalid option');
             }
         } else {
-            $this->expectException('Exception', '', 400);
+            $this->expectException('Exception');
+            $this->expectExceptionMessage('', 400);
         }
         $this->_webApiCall($serviceInfo, ['option' => $optionDataPost]);
     }
@@ -388,8 +391,9 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract
      * @dataProvider optionNegativeUpdateDataProvider
      * @param array $optionData
      * @param string $message
+     * @param int $exceptionCode
      */
-    public function testUpdateNegative($optionData, $message)
+    public function testUpdateNegative($optionData, $message, $exceptionCode)
     {
         $this->_markTestAsRestOnly();
         $productSku = 'simple';
@@ -406,7 +410,9 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract
             ],
         ];
 
-        $this->expectException('Exception', $message, 400);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($message);
+        $this->expectExceptionCode($exceptionCode);
         $this->_webApiCall($serviceInfo, ['option' => $optionData]);
     }
 
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index cd9aaa1d95294297a2fbc4c0a3c8f63697ecf750..cb33edce3af39e6e4a11a9e55e56a1bd921e64ba 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -313,7 +313,8 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
     {
         $sku = $fixtureProduct[ProductInterface::SKU];
         $this->saveProduct($fixtureProduct);
-        $this->expectException('Exception', 'Requested product doesn\'t exist');
+        $this->expectException('Exception');
+        $this->expectExceptionMessage('Requested product doesn\'t exist');
 
         // Delete all with 'all' store code
         $this->deleteProduct($sku);
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_update_negative.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_update_negative.php
index 1e713424f8047e5532f60294b08a05c3d290dfa3..d819fb5f604bf04c3c8827c6a5a69a0068f59a96 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_update_negative.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_update_negative.php
@@ -5,18 +5,31 @@
  */
 
 return [
-    'missed_product_sku' =>
+    'missing_product_sku' => [
         [
-            [
-                'title' => 'title',
-                'type' => 'field',
-                'sort_order' => 1,
-                'is_require' => 1,
-                'price' => 10.0,
-                'price_type' => 'fixed',
-                'sku' => 'sku1',
-                'max_characters' => 10,
-            ],
-            'ProductSku should be specified',
-        ]
+            'title'          => 'title',
+            'type'           => 'field',
+            'sort_order'     => 1,
+            'is_require'     => 1,
+            'price'          => 10.0,
+            'price_type'     => 'fixed',
+            'max_characters' => 10,
+        ],
+        'ProductSku should be specified',
+        400
+    ],
+    'invalid_product_sku' => [
+        [
+            'title'          => 'title',
+            'type'           => 'field',
+            'sort_order'     => 1,
+            'is_require'     => 1,
+            'price'          => 10.0,
+            'price_type'     => 'fixed',
+            'product_sku'            => 'sku1',
+            'max_characters' => 10,
+        ],
+        'Requested product doesn\'t exist',
+        404
+    ],
 ];
diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
index 48cc8b8384d74cb8bb065c1ee3c9e755aa3896e8..452a59d7e702c85865ed6589f18ae4d82fc4831c 100644
--- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
@@ -8,16 +8,12 @@ namespace Magento\Customer\Api;
 use Magento\Customer\Api\Data\CustomerInterface as Customer;
 use Magento\Customer\Model\AccountManagement;
 use Magento\Framework\Exception\InputException;
-use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
+use Magento\Newsletter\Model\Subscriber;
+use Magento\Security\Model\Config;
 use Magento\TestFramework\Helper\Bootstrap;
 use Magento\TestFramework\Helper\Customer as CustomerHelper;
 use Magento\TestFramework\TestCase\WebapiAbstract;
-use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
-use Magento\Security\Model\Config;
-use Magento\Newsletter\Model\Plugin\CustomerPlugin;
-use Magento\Framework\Webapi\Rest\Request as RestRequest;
-use Magento\Newsletter\Model\Subscriber;
-use Magento\Customer\Model\Data\Customer as CustomerData;
 
 /**
  * Test class for Magento\Customer\Api\AccountManagementInterface
@@ -112,16 +108,16 @@ class AccountManagementTest extends WebapiAbstract
         $this->initSubscriber();
 
         if ($this->config->getConfigDataValue(
-            Config::XML_PATH_FRONTED_AREA .
+            Config::XML_PATH_FRONTEND_AREA .
             Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE
         ) != 0) {
             $this->configValue = $this->config
                 ->getConfigDataValue(
-                    Config::XML_PATH_FRONTED_AREA .
+                    Config::XML_PATH_FRONTEND_AREA .
                     Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE
                 );
             $this->config->setDataByPath(
-                Config::XML_PATH_FRONTED_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
+                Config::XML_PATH_FRONTEND_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
                 0
             );
             $this->config->save();
@@ -150,7 +146,7 @@ class AccountManagementTest extends WebapiAbstract
             }
         }
         $this->config->setDataByPath(
-            Config::XML_PATH_FRONTED_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
+            Config::XML_PATH_FRONTEND_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
             $this->configValue
         );
         $this->config->save();
diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/CoreRoutingTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/CoreRoutingTest.php
index 89533a0a624745a69b3b9ddbe6591e868b1a96ff..23b889c7c125140dbfca2d40966a608799ad0b7b 100644
--- a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/CoreRoutingTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/CoreRoutingTest.php
@@ -73,7 +73,8 @@ class CoreRoutingTest extends \Magento\Webapi\Routing\BaseService
                 'operation' => 'testModule3ErrorV1ServiceException',
             ],
         ];
-        $this->expectException('SoapFault', 'Generic service exception');
+        $this->expectException('SoapFault');
+        $this->expectExceptionMessage('Generic service exception');
         $this->_webApiCall($serviceInfo);
     }
 
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Bootstrap/SettingsTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Bootstrap/SettingsTest.php
index aefe51f6ab37c94ed3aa2f1c10309295eec0c4c9..9964ec7f8c508414c7a13f0716b896694f014e09 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Bootstrap/SettingsTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Bootstrap/SettingsTest.php
@@ -199,7 +199,8 @@ class SettingsTest extends \PHPUnit\Framework\TestCase
      */
     public function testGetAsConfigFileException($settingName, $expectedExceptionMsg)
     {
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $expectedExceptionMsg);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage((string)$expectedExceptionMsg);
         $this->_object->getAsConfigFile($settingName);
     }
 
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e8eaf70824db513958f25e29947dc8ad0907912
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Plugin/Model/AttributeSetRepository/RemoveProductsTest.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Plugin\Model\AttributeSetRepository;
+
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
+use Magento\Eav\Api\AttributeSetRepositoryInterface;
+use Magento\Eav\Model\Entity\Attribute\Set;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Interception\PluginList;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Provide tests for RemoveProducts plugin.
+ * @magentoAppArea adminhtml
+ */
+class RemoveProductsTest extends TestCase
+{
+    /**
+     * @return void
+     */
+    public function testRemoveProductsIsRegistered()
+    {
+        $pluginInfo = Bootstrap::getObjectManager()->get(PluginList::class)
+            ->get(AttributeSetRepositoryInterface::class, []);
+        self::assertSame(RemoveProducts::class, $pluginInfo['remove_products']['instance']);
+    }
+
+    /**
+     * Test related to given attribute set products will be removed, if attribute set will be deleted.
+     *
+     * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_product.php
+     */
+    public function testAfterDelete()
+    {
+        $attributeSet = Bootstrap::getObjectManager()->get(Set::class);
+        $attributeSet->load('empty_attribute_set', 'attribute_set_name');
+
+        $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+        $product = $productRepository->get('simple');
+
+        $productCollection = Bootstrap::getObjectManager()->get(CollectionFactory::class)->create();
+        $productCollection->addIdFilter($product->getId());
+        $urlRewriteCollection = Bootstrap::getObjectManager()->get(UrlRewriteCollectionFactory::class)->create();
+        $urlRewriteCollection->addFieldToFilter('entity_type', 'product');
+        $urlRewriteCollection->addFieldToFilter('entity_id', $product->getId());
+
+        self::assertSame(1, $urlRewriteCollection->getSize());
+        self::assertSame(1, $productCollection->getSize());
+
+        $attributeSetRepository = Bootstrap::getObjectManager()->get(AttributeSetRepositoryInterface::class);
+        $attributeSetRepository->deleteById($attributeSet->getAttributeSetId());
+
+        $productCollection = Bootstrap::getObjectManager()->get(CollectionFactory::class)->create();
+        $productCollection->addIdFilter($product->getId());
+        $urlRewriteCollection = Bootstrap::getObjectManager()->get(UrlRewriteCollectionFactory::class)->create();
+        $urlRewriteCollection->addFieldToFilter('entity_type', 'product');
+        $urlRewriteCollection->addFieldToFilter('entity_id', $product->getId());
+
+        self::assertSame(0, $urlRewriteCollection->getSize());
+        self::assertSame(0, $productCollection->getSize());
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product.php
new file mode 100644
index 0000000000000000000000000000000000000000..95f277c7124bd83389452bd933e7fd0e94cac862
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+require __DIR__ . '/../../Eav/_files/empty_attribute_set.php';
+require __DIR__ . '/../../Catalog/_files/product_simple.php';
+
+$product->setAttributeSetId($attributeSet->getId());
+$product->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product_rollback.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd579bdb76f57d44e13bae3963f0f74a8227dff4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_product_rollback.php
@@ -0,0 +1,8 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+require __DIR__ . '/../../Catalog/_files/product_simple_rollback.php';
+require __DIR__ . '/../../Eav/_files/empty_attribute_set_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
index fcbe7c51066171f68287f361f4b0c4e76dd22129..b5ca783d68cf2d115198a91528d512e96ea15886 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
@@ -20,10 +20,11 @@ class ResetPasswordTest extends \Magento\TestFramework\TestCase\AbstractBackendC
     protected $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/';
 
     /**
-     * Checks reset password functionality with default settings and customer reset request event.
+     * Checks reset password functionality with no restrictive settings and customer reset request event.
+     * Admin is not affected by this security check, so reset password email must be sent.
      *
-     * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1
-     * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10
+     * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
+     * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 0
      * @magentoDataFixture Magento/Customer/_files/customer.php
      */
     public function testResetPasswordSuccess()
@@ -40,11 +41,57 @@ class ResetPasswordTest extends \Magento\TestFramework\TestCase\AbstractBackendC
         $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
     }
 
+    /**
+     * Checks reset password functionality with default restrictive min time between
+     * password reset requests and customer reset request event.
+     * Admin is not affected by this security check, so reset password email must be sent.
+     *
+     * @magentoConfigFixture current_store customer/password/max_number_password_reset_requests 0
+     * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 10
+     * @magentoDataFixture Magento/Customer/_files/customer.php
+     */
+    public function testResetPasswordMinTimeError()
+    {
+        $this->passwordResetRequestEventCreate(
+            \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+        );
+        $this->getRequest()->setPostValue(['customer_id' => '1']);
+        $this->dispatch('backend/customer/index/resetPassword');
+        $this->assertSessionMessages(
+            $this->equalTo(['The customer will receive an email with a link to reset password.']),
+            \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+        );
+        $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
+    }
+
+    /**
+     * Checks reset password functionality with default restrictive limited number
+     * password reset requests and customer reset request event.
+     * Admin is not affected by this security check, so reset password email must be sent.
+     *
+     * @magentoConfigFixture current_store customer/password/max_number_password_reset_requests 1
+     * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 0
+     * @magentoDataFixture Magento/Customer/_files/customer.php
+     */
+    public function testResetPasswordLimitError()
+    {
+        $this->passwordResetRequestEventCreate(
+            \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+        );
+        $this->getRequest()->setPostValue(['customer_id' => '1']);
+        $this->dispatch('backend/customer/index/resetPassword');
+        $this->assertSessionMessages(
+            $this->equalTo(['The customer will receive an email with a link to reset password.']),
+            \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+        );
+        $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
+    }
+
     /**
      * Checks reset password functionality with default settings, customer and admin reset request events.
      *
-     * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1
-     * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10
+     * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 1
+     * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 10
      * @magentoConfigFixture current_store contact/email/recipient_email hello@example.com
      * @magentoDataFixture Magento/Customer/_files/customer.php
      */
@@ -59,10 +106,8 @@ class ResetPasswordTest extends \Magento\TestFramework\TestCase\AbstractBackendC
         $this->getRequest()->setPostValue(['customer_id' => '1']);
         $this->dispatch('backend/customer/index/resetPassword');
         $this->assertSessionMessages(
-            $this->equalTo(
-                ['Too many password reset requests. Please wait and try again or contact hello@example.com.']
-            ),
-            \Magento\Framework\Message\MessageInterface::TYPE_ERROR
+            $this->equalTo(['The customer will receive an email with a link to reset password.']),
+            \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
         );
         $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
     }
diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Design/Fallback/RulePoolTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Design/Fallback/RulePoolTest.php
index 1500c91478a4a05b4a9f4ccae5d48c82f5a05ff3..c586c68b74573da504c9f7c9d36cf2566034eaec 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/View/Design/Fallback/RulePoolTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/View/Design/Fallback/RulePoolTest.php
@@ -74,7 +74,8 @@ class RulePoolTest extends \PHPUnit\Framework\TestCase
      */
     public function testGetPatternDirsException($type, array $overriddenParams, $expectedErrorMessage)
     {
-        $this->expectException('InvalidArgumentException', $expectedErrorMessage);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($expectedErrorMessage);
         $params = $overriddenParams + $this->defaultParams;
         $this->model->getRule($type)->getPatternDirs($params);
     }
diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php
index c014b517f646354f43e2f495a12fa3f67a7f0942..66e8eb3e453f9264669376fa497acebf36033ec4 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/View/LayoutTest.php
@@ -275,7 +275,8 @@ class LayoutTest extends \PHPUnit\Framework\TestCase
         $msg = 'Html tag "span" is forbidden for usage in containers. ' .
             'Consider to use one of the allowed: aside, dd, div, dl, fieldset, main, nav, ' .
             'header, footer, ol, p, section, table, tfoot, ul.';
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $msg);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($msg);
         $this->_layout->addContainer('container', 'Container', ['htmlTag' => 'span']);
     }
 
diff --git a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/RatingTest.php b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/RatingTest.php
index c88bd5ed7cf77060a6d9a57616615f14dbde6ed2..7801cb2c78c2436aff71f8fbdfbfc8da22132f98 100644
--- a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/RatingTest.php
+++ b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/RatingTest.php
@@ -77,7 +77,8 @@ class RatingTest extends \PHPUnit\Framework\TestCase
      */
     public function testRatingSaveWithError()
     {
-        $this->expectException('Exception', 'Rolled back transaction has not been completed correctly');
+        $this->expectException('Exception');
+        $this->expectExceptionMessage('Rolled back transaction has not been completed correctly');
         $rating = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
             \Magento\Review\Model\Rating::class
         );
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/ResolverTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/ResolverTest.php
index f4560ed31ae49b274b499d90416f04b727675299..33b6ab7e99aedb0575cf287129b672b95af9c92f 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/ResolverTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/ResolverTest.php
@@ -40,7 +40,8 @@ class ResolverTest extends \PHPUnit\Framework\TestCase
 
     public function testGetTagsForNotObject()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument is not an object');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument is not an object');
         $this->model->getTags('some scalar');
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/DummyTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/DummyTest.php
index ad043260645875033aaf905c6e15a20de5546f0f..4f072e037f74eb92a9593d6334343682311f4518 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/DummyTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/DummyTest.php
@@ -20,7 +20,8 @@ class DummyTest extends \PHPUnit\Framework\TestCase
 
     public function testGetTagsWithScalar()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument is not an object');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument is not an object');
         $this->model->getTags('scalar');
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/FactoryTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/FactoryTest.php
index 8964bd70f0ba8aba952e7c447fe9c31c2868e2fc..3e96c7ab9ca6caa5f84a40b1564e231a801017d0 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/FactoryTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/FactoryTest.php
@@ -49,7 +49,8 @@ class FactoryTest extends \PHPUnit\Framework\TestCase
 
     public function testGetStrategyWithScalar()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument is not an object');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument is not an object');
         $this->model->getStrategy('some scalar');
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/IdentifierTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/IdentifierTest.php
index d0fcf9d8a739deccc515ed9bbb659e198417138c..4dc46d46e675ed22fda22f6dbbc04831d471da78 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/IdentifierTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/IdentifierTest.php
@@ -22,7 +22,8 @@ class IdentifierTest extends \PHPUnit\Framework\TestCase
 
     public function testGetWithScalar()
     {
-        $this->expectException(\InvalidArgumentException::class, 'Provided argument is not an object');
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Provided argument is not an object');
         $this->model->getTags('scalar');
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php
index 53012558188001c384957d86c9e200297908f14c..daf3a4bdfab0c6e3979e9848235935e216718b48 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php
@@ -54,7 +54,8 @@ class ErrorHandlerTest extends \PHPUnit\Framework\TestCase
         $errorLine = 'test_error_line';
 
         $exceptedExceptionMessage = sprintf('%s: %s in %s on line %s', $errorPhrase, $errorStr, $errorFile, $errorLine);
-        $this->expectException('Exception', $exceptedExceptionMessage);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($exceptedExceptionMessage);
 
         $this->object->handler($errorNo, $errorStr, $errorFile, $errorLine);
     }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php b/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
index a209c313a0a897808d05e46ae762cd4c1ce5a7e2..3db75f7ec7fb29f5e956e2da9c663c18ec2e93d1 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/SetupInfoTest.php
@@ -24,7 +24,8 @@ class SetupInfoTest extends \PHPUnit\Framework\TestCase
      */
     public function testConstructorExceptions($server, $expectedError)
     {
-        $this->expectException('\InvalidArgumentException', $expectedError);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($expectedError);
         new SetupInfo($server);
     }
 
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ShellTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ShellTest.php
index 9eed1fbedd95473918f712151fa043c520dd4401..65ac19cbc29967cb1a8314c8cf6d6fca7e82ffd3 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/ShellTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/ShellTest.php
@@ -69,7 +69,8 @@ class ShellTest extends \PHPUnit\Framework\TestCase
         );
         $this->driverMock->expects($this->once())->method('execute')->willReturn($response);
         $this->loggerMock->expects($this->once())->method('error')->with($logEntry);
-        $this->expectException(LocalizedException::class, "Command returned non-zero exit code:\n`$command`");
+        $this->expectException(LocalizedException::class);
+        $this->expectExceptionMessage("Command returned non-zero exit code:\n`$command`");
         $this->model->execute($command, []);
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/View/Asset/MaterializationStrategy/FactoryTest.php b/lib/internal/Magento/Framework/App/Test/Unit/View/Asset/MaterializationStrategy/FactoryTest.php
index c7a276454535792618ee8ec81dc90d6886f1a48f..1873cc593a65551e1ad8efafb37a84209d23a273 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/View/Asset/MaterializationStrategy/FactoryTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/View/Asset/MaterializationStrategy/FactoryTest.php
@@ -87,7 +87,8 @@ class FactoryTest extends \PHPUnit\Framework\TestCase
 
         $factory = new Factory($this->objectManager, []);
 
-        $this->expectException('LogicException', 'No materialization strategy is supported');
+        $this->expectException('LogicException');
+        $this->expectExceptionMessage('No materialization strategy is supported');
         $factory->create($asset);
     }
 
diff --git a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php
index ee00a2154f4156ab8e5d254d9c26b92acd2ea9a0..129fade7b4a9cfb8312e47287bed49f483b919d0 100644
--- a/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php
+++ b/lib/internal/Magento/Framework/Cache/Test/Unit/Frontend/Adapter/ZendTest.php
@@ -80,7 +80,8 @@ class ZendTest extends \PHPUnit\Framework\TestCase
      */
     public function testCleanException($cleaningMode, $expectedErrorMessage)
     {
-        $this->expectException('InvalidArgumentException', $expectedErrorMessage);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($expectedErrorMessage);
         $object = new \Magento\Framework\Cache\Frontend\Adapter\Zend($this->createMock(\Zend_Cache_Core::class));
         $object->clean($cleaningMode);
     }
diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/InterfaceGeneratorTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/InterfaceGeneratorTest.php
index 347bc6b46ace53f6790ff07be877e8a296a0121e..0f3daa46e1ec39f248ff9b677d9304dfec9072aa 100644
--- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/InterfaceGeneratorTest.php
+++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/InterfaceGeneratorTest.php
@@ -75,7 +75,8 @@ class InterfaceGeneratorTest extends \PHPUnit\Framework\TestCase
     public function testGenerate($additionalMethodsData, $expectedException, $expectedExceptionMessage)
     {
         if ($expectedException) {
-            $this->expectException($expectedException, $expectedExceptionMessage);
+            $this->expectException($expectedException);
+            $this->expectExceptionMessage($expectedExceptionMessage);
         }
         $methodsData = array_merge_recursive($this->methodsData, $additionalMethodsData);
         $this->interfaceGenerator->setClassDocBlock($this->interfaceDocBlock)
diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/IoTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/IoTest.php
index bc23ef954f21652d383ec51c7f605100411593ab..9c63de1258d150a623a256025332873d2a4933f4 100644
--- a/lib/internal/Magento/Framework/Code/Test/Unit/Generator/IoTest.php
+++ b/lib/internal/Magento/Framework/Code/Test/Unit/Generator/IoTest.php
@@ -97,7 +97,8 @@ class IoTest extends \PHPUnit\Framework\TestCase
         } else {
             $exceptionMessage = 'Some error renaming file';
             $renameMockEvent = $this->throwException(new FileSystemException(new Phrase($exceptionMessage)));
-            $this->expectException(\Magento\Framework\Exception\FileSystemException::class, $exceptionMessage);
+            $this->expectException(\Magento\Framework\Exception\FileSystemException::class);
+            $this->expectExceptionMessage($exceptionMessage);
         }
 
         $this->_filesystemDriverMock->expects($this->once())
diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Validator/ArgumentSequenceTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Validator/ArgumentSequenceTest.php
index d1692fd4ec012718158861669cd6813cfdef9526..96be42658f76228cb86013fe8439e07459e34c8b 100644
--- a/lib/internal/Magento/Framework/Code/Test/Unit/Validator/ArgumentSequenceTest.php
+++ b/lib/internal/Magento/Framework/Code/Test/Unit/Validator/ArgumentSequenceTest.php
@@ -51,7 +51,8 @@ class ArgumentSequenceTest extends \PHPUnit\Framework\TestCase
             'Actual  : %s' .
             PHP_EOL;
         $message = sprintf($message, '\ArgumentSequence\InvalidChildClass', $expectedSequence, $actualSequence);
-        $this->expectException(\Magento\Framework\Exception\ValidatorException::class, $message);
+        $this->expectException(\Magento\Framework\Exception\ValidatorException::class);
+        $this->expectExceptionMessage($message);
         $this->_validator->validate('\ArgumentSequence\InvalidChildClass');
     }
 }
diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Validator/TypeDuplicationTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Validator/TypeDuplicationTest.php
index 3822d148adca5d84de774b18e8ba0804d68b4b98..a82c88e3e18b1f8fa8972441b106539120187567 100644
--- a/lib/internal/Magento/Framework/Code/Test/Unit/Validator/TypeDuplicationTest.php
+++ b/lib/internal/Magento/Framework/Code/Test/Unit/Validator/TypeDuplicationTest.php
@@ -49,7 +49,8 @@ class TypeDuplicationTest extends \PHPUnit\Framework\TestCase
             $this->_fixturePath .
             PHP_EOL .
             'Multiple type injection [\TypeDuplication\ArgumentBaseClass]';
-        $this->expectException(\Magento\Framework\Exception\ValidatorException::class, $message);
+        $this->expectException(\Magento\Framework\Exception\ValidatorException::class);
+        $this->expectExceptionMessage($message);
         $this->_validator->validate('\TypeDuplication\InvalidClassWithDuplicatedTypes');
     }
 }
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/Data/ConfigDataTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/Data/ConfigDataTest.php
index b222f52dc738bfb4c173b27d59bab613e1d42cad..619135f9c7038a6e391f16af4c14492feac08866 100644
--- a/lib/internal/Magento/Framework/Config/Test/Unit/Data/ConfigDataTest.php
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/Data/ConfigDataTest.php
@@ -42,7 +42,8 @@ class ConfigDataTest extends \PHPUnit\Framework\TestCase
 
         $configData = new ConfigData('testKey');
 
-        $this->expectException('InvalidArgumentException', $expectedException);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($expectedException);
         $configData->set($key, 'value');
     }
 
diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php
index 8d3623d485b89ef3bc8ed5c34fa5cf5c9cad5c45..92c901f3872f299347b156fec42a31dd720e1217 100644
--- a/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php
+++ b/lib/internal/Magento/Framework/DB/Test/Unit/Tree/NodeTest.php
@@ -20,7 +20,8 @@ class NodeTest extends \PHPUnit\Framework\TestCase
         $expectedException,
         $expectedExceptionMessage
     ) {
-        $this->expectException($expectedException, $expectedExceptionMessage);
+        $this->expectException($expectedException);
+        $this->expectExceptionMessage($expectedExceptionMessage);
         new \Magento\Framework\DB\Tree\Node($data['node_data'], $data['keys']);
     }
 
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/CompositeTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/CompositeTest.php
index 768b8d3b9efa2e7aa30872b95a6a7311ebc48109..bca8bb0d9347f8195348c5a2a450f2589208beae 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/CompositeTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Argument/Interpreter/CompositeTest.php
@@ -55,7 +55,8 @@ class CompositeTest extends \PHPUnit\Framework\TestCase
      */
     public function testEvaluateWrongDiscriminator($input, $expectedExceptionMessage)
     {
-        $this->expectException('\InvalidArgumentException', $expectedExceptionMessage);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $this->_model->evaluate($input);
     }
 
diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php
index c3316db7be01651da895f874aef9a43d468421d6..07de9941271c33de5d7ddab60868f2b040d6ec41 100644
--- a/lib/internal/Magento/Framework/File/Uploader.php
+++ b/lib/internal/Magento/Framework/File/Uploader.php
@@ -201,7 +201,7 @@ class Uploader
         if ($this->_enableFilesDispersion) {
             $fileName = $this->correctFileNameCase($fileName);
             $this->setAllowCreateFolders(true);
-            $this->_dispretionPath = self::getDispretionPath($fileName);
+            $this->_dispretionPath = self::getDispersionPath($fileName);
             $destinationFile .= $this->_dispretionPath;
             $this->_createDestinationFolder($destinationFile);
         }
@@ -610,8 +610,20 @@ class Uploader
      *
      * @param string $fileName
      * @return string
+     * @deprecated
      */
     public static function getDispretionPath($fileName)
+    {
+        return self::getDispersionPath($fileName);
+    }
+
+    /**
+     * Get dispertion path
+     *
+     * @param string $fileName
+     * @return string
+     */
+    public static function getDispersionPath($fileName)
     {
         $char = 0;
         $dispertionPath = '';
diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/DirectoryListTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/DirectoryListTest.php
index 8a96f79179bd7ec271031d327b04e45e4aef56dc..96b56de8451c278e26f891d6ad128b97ed48d3ca 100644
--- a/lib/internal/Magento/Framework/Filesystem/Test/Unit/DirectoryListTest.php
+++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/DirectoryListTest.php
@@ -21,7 +21,8 @@ class DirectoryListTest extends \PHPUnit\Framework\TestCase
      */
     public function testValidate($config, $expectedError)
     {
-        $this->expectException('\InvalidArgumentException', $expectedError);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($expectedError);
         DirectoryList::validate($config);
     }
 
diff --git a/lib/internal/Magento/Framework/Image/Test/Unit/Adapter/ImageMagickTest.php b/lib/internal/Magento/Framework/Image/Test/Unit/Adapter/ImageMagickTest.php
index af44ae45c2cc5840a6e12ae28d0424195a4abdb6..ae0348f489fd2b3d3ddc25dcd0b64c59a3bfe582 100644
--- a/lib/internal/Magento/Framework/Image/Test/Unit/Adapter/ImageMagickTest.php
+++ b/lib/internal/Magento/Framework/Image/Test/Unit/Adapter/ImageMagickTest.php
@@ -58,7 +58,8 @@ class ImageMagickTest extends \PHPUnit\Framework\TestCase
      */
     public function testWatermark($imagePath, $expectedMessage)
     {
-        $this->expectException('LogicException', $expectedMessage);
+        $this->expectException('LogicException');
+        $this->expectExceptionMessage($expectedMessage);
         $this->imageMagic->watermark($imagePath);
     }
 
diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
index e278de0fff914dd2c2b8d2f3dd0d4d0e65795e19..c91b56560eb75f2b95bb2a2a0f722e15bc17672b 100644
--- a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
+++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
@@ -124,7 +124,8 @@ class ChangelogTest extends \PHPUnit\Framework\TestCase
         $this->mockIsTableExists($changelogTableName, false);
         $this->mockGetTableName();
 
-        $this->expectException('Exception', "Table {$changelogTableName} does not exist");
+        $this->expectException('Exception');
+        $this->expectExceptionMessage("Table {$changelogTableName} does not exist");
         $this->model->setViewId('viewIdtest');
         $this->model->getVersion();
     }
@@ -135,7 +136,8 @@ class ChangelogTest extends \PHPUnit\Framework\TestCase
         $this->mockIsTableExists($changelogTableName, false);
         $this->mockGetTableName();
 
-        $this->expectException('Exception', "Table {$changelogTableName} does not exist");
+        $this->expectException('Exception');
+        $this->expectExceptionMessage("Table {$changelogTableName} does not exist");
         $this->model->setViewId('viewIdtest');
         $this->model->drop();
     }
@@ -226,7 +228,8 @@ class ChangelogTest extends \PHPUnit\Framework\TestCase
         $this->mockIsTableExists($changelogTableName, false);
         $this->mockGetTableName();
 
-        $this->expectException('Exception', "Table {$changelogTableName} does not exist");
+        $this->expectException('Exception');
+        $this->expectExceptionMessage("Table {$changelogTableName} does not exist");
         $this->model->setViewId('viewIdtest');
         $this->model->getList(mt_rand(1, 200), mt_rand(201, 400));
     }
@@ -237,7 +240,8 @@ class ChangelogTest extends \PHPUnit\Framework\TestCase
         $this->mockIsTableExists($changelogTableName, false);
         $this->mockGetTableName();
 
-        $this->expectException('Exception', "Table {$changelogTableName} does not exist");
+        $this->expectException('Exception');
+        $this->expectExceptionMessage("Table {$changelogTableName} does not exist");
         $this->model->setViewId('viewIdtest');
         $this->model->clear(mt_rand(1, 200));
     }
diff --git a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/CompositeTest.php b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/CompositeTest.php
index 57d8841455fd72bd7be7e0030a22d3890feeb4fc..e302dc8f5ad5e93bc202056fa260f4eab5c641c1 100644
--- a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/CompositeTest.php
+++ b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/CompositeTest.php
@@ -85,7 +85,8 @@ class CompositeTest extends \PHPUnit\Framework\TestCase
             ->method('render')
             ->willThrowException($exception);
 
-        $this->expectException('Exception', $message);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($message);
         $this->object->render(['text'], []);
     }
 }
diff --git a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/InlineTest.php b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/InlineTest.php
index 793557000fb1e253b2941a1f4156d0d486edf583..f9b6e47c19a8634943f3b76e1633fb82ed954c48 100644
--- a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/InlineTest.php
+++ b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/InlineTest.php
@@ -88,7 +88,8 @@ class InlineTest extends \PHPUnit\Framework\TestCase
             ->method('get')
             ->willThrowException($exception);
 
-        $this->expectException('Exception', $message);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($message);
         $this->renderer->render(['text'], []);
     }
 }
diff --git a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/TranslateTest.php b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/TranslateTest.php
index fb4b3232cab31626f96585c9a5e8170f13b3fc40..d8a0b3673ad6d5f5bf8953d444a4fd9b19a93277 100644
--- a/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/TranslateTest.php
+++ b/lib/internal/Magento/Framework/Phrase/Test/Unit/Renderer/TranslateTest.php
@@ -91,7 +91,8 @@ class TranslateTest extends \PHPUnit\Framework\TestCase
             ->method('getData')
             ->willThrowException($exception);
 
-        $this->expectException('Exception', $message);
+        $this->expectException('Exception');
+        $this->expectExceptionMessage($message);
         $this->_renderer->render(['text'], []);
     }
 }
diff --git a/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php b/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php
index 201856124d7219fd024af2c79caba1ae44ee4ce3..1516f65479771ef571ef81b189e0181aa1ae66ce 100644
--- a/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php
+++ b/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php
@@ -114,7 +114,7 @@ class DbStatusValidatorTest extends \PHPUnit\Framework\TestCase
         $this->cacheMock->expects(static::never())
             ->method('save');
 
-        $this->expectException(LocalizedException::class, $expectedMessage);
+        $this->expectException(LocalizedException::class);
         $this->expectExceptionMessage($expectedMessage);
         $this->plugin->beforeDispatch($this->frontControllerMock, $this->requestMock);
     }
diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/BuilderTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/BuilderTest.php
index 2df8d535ee788572efc5ff49dbeedd969ca282da..860d449c4717e129b8b2fe2362675295392b1148 100644
--- a/lib/internal/Magento/Framework/Validator/Test/Unit/BuilderTest.php
+++ b/lib/internal/Magento/Framework/Validator/Test/Unit/BuilderTest.php
@@ -341,7 +341,8 @@ class BuilderTest extends \PHPUnit\Framework\TestCase
      */
     public function testConstructorConfigValidation(array $options, $exception, $exceptionMessage)
     {
-        $this->expectException($exception, $exceptionMessage);
+        $this->expectException($exception);
+        $this->expectExceptionMessage($exceptionMessage);
         if (array_key_exists('method', $options)) {
             $options = ['methods' => [$options]];
         }
@@ -362,7 +363,8 @@ class BuilderTest extends \PHPUnit\Framework\TestCase
      */
     public function testAddConfigurationConfigValidation(array $options, $exception, $exceptionMessage)
     {
-        $this->expectException($exception, $exceptionMessage);
+        $this->expectException($exception);
+        $this->expectExceptionMessage($exceptionMessage);
 
         $constraints = [
             ['alias' => 'alias', 'class' => 'Some\Validator\Class', 'options' => null, 'type' => 'entity'],
diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/Constraint/Option/CallbackTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/Constraint/Option/CallbackTest.php
index 91bd3a7608d6777c54a6d2f13b5952234e520e8d..9617b28383088fd2fa5d67de54c94fb9896e5766 100644
--- a/lib/internal/Magento/Framework/Validator/Test/Unit/Constraint/Option/CallbackTest.php
+++ b/lib/internal/Magento/Framework/Validator/Test/Unit/Constraint/Option/CallbackTest.php
@@ -123,7 +123,8 @@ class CallbackTest extends \PHPUnit\Framework\TestCase
     public function testGetValueException($callback, $expectedMessage, $createInstance = false)
     {
         $option = new \Magento\Framework\Validator\Constraint\Option\Callback($callback, null, $createInstance);
-        $this->expectException('InvalidArgumentException', $expectedMessage);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($expectedMessage);
         $option->getValue();
     }
 
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/TemplateTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/TemplateTest.php
index 83813785886c5dc8cd8315d55ab8449c7c27a0ee..b457a98b5e2366fb9f6ae6f79b7ba6e828a7ed35 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Element/TemplateTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/TemplateTest.php
@@ -175,7 +175,8 @@ class TemplateTest extends \PHPUnit\Framework\TestCase
             ->method('getMode')
             ->willReturn(\Magento\Framework\App\State::MODE_DEVELOPER);
 
-        $this->expectException(\Magento\Framework\Exception\ValidatorException::class, $exception);
+        $this->expectException(\Magento\Framework\Exception\ValidatorException::class);
+        $this->expectExceptionMessage($exception);
         $this->block->fetchView($template);
     }
 
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/Override/ThemeModularTest.php b/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/Override/ThemeModularTest.php
index d1a1851c06c52ff84361ece78f7d409430f19788..cae621a09125ff4152ee15d88bfe20dbb261a4a2 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/Override/ThemeModularTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/Override/ThemeModularTest.php
@@ -169,7 +169,8 @@ class ThemeModularTest extends \PHPUnit\Framework\TestCase
         $filePath = 'design/area/theme_path/Module_One/override/theme/vendor/parent_theme/1.xml';
         $expectedMessage = "Trying to override modular view file '$filePath' for theme 'vendor/parent_theme'"
             . ", which is not ancestor of theme 'vendor/theme_path'";
-        $this->expectException(\Magento\Framework\Exception\LocalizedException::class, $expectedMessage);
+        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+        $this->expectExceptionMessage($expectedMessage);
 
         $theme = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class);
         $theme->expects($this->once())->method('getFullPath')->willReturn($themePath);
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/HelperMethodTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/HelperMethodTest.php
index 19b450e2d4235813198cd0ec5e192d56a2396471..458b23a4b15eb54b92b6f9b47eb43582ce915f57 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/HelperMethodTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/HelperMethodTest.php
@@ -67,7 +67,8 @@ class HelperMethodTest extends \PHPUnit\Framework\TestCase
      */
     public function testEvaluateException($helperMethod, $expectedExceptionMessage)
     {
-        $this->expectException('\InvalidArgumentException', $expectedExceptionMessage);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $input = ['value' => 'some text', 'helper' => $helperMethod];
         $this->_model->evaluate($input);
     }
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/NamedParamsTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/NamedParamsTest.php
index 65f72ef96f85093fa39daa0ac07b912fefd7a9a3..5ae0b0332f28a8776147cb5a157d734e5f37d6d2 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/NamedParamsTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/NamedParamsTest.php
@@ -62,7 +62,8 @@ class NamedParamsTest extends \PHPUnit\Framework\TestCase
      */
     public function testEvaluateWrongParam($input, $expectedExceptionMessage)
     {
-        $this->expectException('\InvalidArgumentException', $expectedExceptionMessage);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $this->_model->evaluate($input);
     }
 
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/ObjectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/ObjectTest.php
index 682106e01ad4ed663476a4b82416d94eb3b1e4df..7cc280a930d9c6f7677e66382933d022d67f0e39 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/ObjectTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/ObjectTest.php
@@ -49,7 +49,8 @@ class ObjectTest extends \PHPUnit\Framework\TestCase
      */
     public function testEvaluateWrongClass($input, $expectedException, $expectedExceptionMessage)
     {
-        $this->expectException($expectedException, $expectedExceptionMessage);
+        $this->expectException($expectedException);
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $self = $this;
         $this->_objectManager->expects($this->any())->method('create')->willReturnCallback(
             function ($className) use ($self) {
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php
index ffb79790d33f8ba4474ebeb76001044334098b53..d3e91cb7c2b7efe78591c7d2c5797f443a8d14ab 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php
@@ -67,7 +67,8 @@ class OptionsTest extends \PHPUnit\Framework\TestCase
      */
     public function testEvaluateWrongModel($input, $expectedException, $expectedExceptionMessage)
     {
-        $this->expectException($expectedException, $expectedExceptionMessage);
+        $this->expectException($expectedException);
+        $this->expectExceptionMessage($expectedExceptionMessage);
         $this->_model->evaluate($input);
     }
 
diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/JsonTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/JsonTest.php
index 71ede3fd4fcb4ae12d3ee86bb8d586a49d5ac2ab..d4177ceee8d7ef4f617c73c4277b2ca6d6f3b819 100644
--- a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/JsonTest.php
+++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/JsonTest.php
@@ -55,7 +55,8 @@ class JsonTest extends \PHPUnit\Framework\TestCase
 
     public function testDeserializerInvalidArgumentException()
     {
-        $this->expectException('InvalidArgumentException', '"boolean" data type is invalid. String is expected.');
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage('"boolean" data type is invalid. String is expected.');
         $this->_jsonDeserializer->deserialize(false);
     }
 
diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/XmlTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/XmlTest.php
index 2c754f23b0b5cb6d5c86dc03c9783bc91f56754c..4b9c90de7355e2edc681104e60cadb7b28842549 100644
--- a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/XmlTest.php
+++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/Deserializer/XmlTest.php
@@ -42,7 +42,8 @@ class XmlTest extends \PHPUnit\Framework\TestCase
 
     public function testDeserializeInvalidArgumentException()
     {
-        $this->expectException('InvalidArgumentException', '"boolean" data type is invalid. String is expected.');
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage('"boolean" data type is invalid. String is expected.');
         $this->_xmlDeserializer->deserialize(false);
     }
 
diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/DeserializerFactoryTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/DeserializerFactoryTest.php
index 74d87095823f743f6b94cb64c083167a3956a379..588a67430a61f4cbde40d684b3b7a38a45b8c6ea 100644
--- a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/DeserializerFactoryTest.php
+++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Request/DeserializerFactoryTest.php
@@ -11,7 +11,8 @@ class DeserializerFactoryTest extends \PHPUnit\Framework\TestCase
 {
     public function testGetLogicExceptionEmptyRequestAdapter()
     {
-        $this->expectException('LogicException', 'Request deserializer adapter is not set.');
+        $this->expectException('LogicException');
+        $this->expectExceptionMessage('Request deserializer adapter is not set.');
         $interpreterFactory = new \Magento\Framework\Webapi\Rest\Request\DeserializerFactory(
             $this->createMock(\Magento\Framework\ObjectManagerInterface::class),
             []
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Compiler/Config/ModificationChainTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Compiler/Config/ModificationChainTest.php
index dbe566f9a1c7a49631e005829b45891ea69b3c5a..7f0553034b4f95c0a57889f05a3519bb29200010 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Compiler/Config/ModificationChainTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Compiler/Config/ModificationChainTest.php
@@ -25,7 +25,8 @@ class ModificationChainTest extends \PHPUnit\Framework\TestCase
 
     public function testConstructorException()
     {
-        $this->expectException('InvalidArgumentException', 'Wrong modifier provided');
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage('Wrong modifier provided');
         $modificationsList = [];
         $modificationsList[] = $this->getMockBuilder(
             \Magento\Setup\Module\Di\Compiler\Config\ModificationInterface::class
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Options/ResolverTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Options/ResolverTest.php
index cb49c3a33a5c551d976ed0d5576b86c786ba3bce..331b2b8705c5ba9767038b9191d8446650522197 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Options/ResolverTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Options/ResolverTest.php
@@ -136,7 +136,8 @@ class ResolverTest extends \PHPUnit\Framework\TestCase
                 'directoryList' => $directoryList
             ]
         );
-        $this->expectException('\InvalidArgumentException', $message);
+        $this->expectException('\InvalidArgumentException');
+        $this->expectExceptionMessage($message);
         $resolver->getOptions();
     }
 
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/PhraseTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/PhraseTest.php
index e87a716ffdb6265feca6987a1fbda1c9c3ed7c7e..b76cc4a0b1f1a47fb39d0c59bc40ca189910665b 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/PhraseTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/PhraseTest.php
@@ -55,7 +55,8 @@ class PhraseTest extends \PHPUnit\Framework\TestCase
      */
     public function testWrongParametersWhilePhraseCreation($constructArguments, $message)
     {
-        $this->expectException('DomainException', $message);
+        $this->expectException('DomainException');
+        $this->expectExceptionMessage($message);
 
         new Phrase(...array_values($constructArguments));
     }
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php
index 3395596f399a31ede3f2db43e5a7e317e8b06bc4..1c035e8ceed8225199789768d24951f93d5a9679 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php
@@ -111,7 +111,8 @@ class GeneratorTest extends \PHPUnit\Framework\TestCase
         $error = "Duplicated translation is found, but it is not allowed.\n"
             . "The phrase \"phrase1\" is translated in 1 places.\n"
             . "The phrase \"phrase2\" is translated in 1 places.\n";
-        $this->expectException('\RuntimeException', $error);
+        $this->expectException('\RuntimeException');
+        $this->expectExceptionMessage($error);
 
         $allowDuplicates = false;
 
diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/AbstractParserTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/AbstractParserTest.php
index e68a1c624376b42e83c9aec2a5eec0bff3a718f3..3c744bb44d32a131ac44d7c99765ab5aa930a6b0 100644
--- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/AbstractParserTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/AbstractParserTest.php
@@ -29,7 +29,8 @@ class AbstractParserTest extends \PHPUnit\Framework\TestCase
      */
     public function testValidateOptions($options, $message)
     {
-        $this->expectException('InvalidArgumentException', $message);
+        $this->expectException('InvalidArgumentException');
+        $this->expectExceptionMessage($message);
 
         $this->_parserMock->addAdapter(
             'php',