diff --git a/composer.json b/composer.json index f980005c1..3e3b0ab42 100755 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", "ext-curl": "*", "allure-framework/allure-codeception": "~1.3.0", - "codeception/codeception": "~2.3.4 || ~2.4.0 ", + "codeception/codeception": "~2.4.5", "composer/composer": "^1.6", "consolidation/robo": "^1.0.0", "csharpru/vault-php": "~3.5.3", diff --git a/composer.lock b/composer.lock index 275ae9778..5c2ba8c2d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "beb8473a3c21b83da864289149fc03b4", + "content-hash": "1b0ef9b803188c23577751eba2a3d966", "packages": [ { "name": "allure-framework/allure-codeception", @@ -263,31 +263,28 @@ }, { "name": "codeception/codeception", - "version": "2.3.9", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -353,27 +350,70 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-08-01T07:21:49+00:00" }, { - "name": "codeception/stub", - "version": "1.0.4", + "name": "codeception/phpunit-wrapper", + "version": "6.7.0", "source": { "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "93f59e028826464eac086052fa226e58967f6907" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/93f59e028826464eac086052fa226e58967f6907", + "reference": "93f59e028826464eac086052fa226e58967f6907", "shasum": "" }, "require": { - "phpunit/phpunit-mock-objects": ">2.3 <7.0" + "phpunit/php-code-coverage": ">=4.0.4 <6.0", + "phpunit/phpunit": ">=6.5.13 <7.0", + "sebastian/comparator": ">=1.2.4 <3.0", + "sebastian/diff": ">=1.4 <4.0" + }, + "replace": { + "codeception/phpunit-wrapper": "*" }, "require-dev": { - "phpunit/phpunit": ">=4.8 <8.0" + "codeception/specify": "*", + "vlucas/phpdotenv": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2019-08-18T15:43:35+00:00" + }, + { + "name": "codeception/stub", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "853657f988942f7afb69becf3fd0059f192c705a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/853657f988942f7afb69becf3fd0059f192c705a", + "reference": "853657f988942f7afb69becf3fd0059f192c705a", + "shasum": "" + }, + "require": { + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3" }, "type": "library", "autoload": { @@ -386,7 +426,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" + "time": "2019-03-02T15:35:10+00:00" }, { "name": "composer/ca-bundle", diff --git a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php index 9fe7adb7a..07153040c 100644 --- a/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php +++ b/src/Magento/FunctionalTestingFramework/Extension/TestContextExtension.php @@ -13,12 +13,18 @@ /** * Class TestContextExtension * @SuppressWarnings(PHPMD.UnusedPrivateField) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TestContextExtension extends BaseExtension { const TEST_PHASE_AFTER = "_after"; - const CODECEPT_AFTER_VERSION = "2.3.9"; + const TEST_PHASE_BEFORE = "_before"; + const TEST_FAILED_FILE = 'failed'; + const TEST_HOOKS = [ + self::TEST_PHASE_AFTER => 'AfterHook', + self::TEST_PHASE_BEFORE => 'BeforeHook' + ]; /** * Codeception Events Mapping to methods @@ -36,7 +42,6 @@ public function _initialize() { $events = [ Events::TEST_START => 'testStart', - Events::TEST_FAIL => 'testFail', Events::STEP_AFTER => 'afterStep', Events::TEST_END => 'testEnd', Events::RESULT_PRINT_AFTER => 'saveFailed' @@ -57,23 +62,7 @@ public function testStart() } /** - * Codeception event listener function, triggered on test failure. - * @param \Codeception\Event\FailEvent $e - * @return void - */ - public function testFail(\Codeception\Event\FailEvent $e) - { - $cest = $e->getTest(); - $context = $this->extractContext($e->getFail()->getTrace(), $cest->getTestMethod()); - // Do not attempt to run _after if failure was in the _after block - // Try to run _after but catch exceptions to prevent them from overwriting original failure. - if ($context != TestContextExtension::TEST_PHASE_AFTER) { - $this->runAfterBlock($e, $cest); - } - } - - /** - * Codeception event listener function, triggered on test ending (naturally or by error). + * Codeception event listener function, triggered on test ending naturally or by errors/failures. * @param \Codeception\Event\TestEvent $e * @return void * @throws \Exception @@ -82,55 +71,33 @@ public function testEnd(\Codeception\Event\TestEvent $e) { $cest = $e->getTest(); - //Access private TestResultObject to find stack and if there are any errors (as opposed to failures) + //Access private TestResultObject to find stack and if there are any errors/failures $testResultObject = call_user_func(\Closure::bind( function () use ($cest) { return $cest->getTestResultObject(); }, $cest )); - $errors = $testResultObject->errors(); - if (!empty($errors)) { - foreach ($errors as $error) { - if ($error->failedTest()->getTestMethod() == $cest->getName()) { - $stack = $errors[0]->thrownException()->getTrace(); - $context = $this->extractContext($stack, $cest->getTestMethod()); - // Do not attempt to run _after if failure was in the _after block - // Try to run _after but catch exceptions to prevent them from overwriting original failure. - if ($context != TestContextExtension::TEST_PHASE_AFTER) { - $this->runAfterBlock($e, $cest); - } - continue; + + // check for errors in all test hooks and attach in allure + if (!empty($testResultObject->errors())) { + foreach ($testResultObject->errors() as $error) { + if ($error->failedTest()->getTestMethod() == $cest->getTestMethod()) { + $this->attachExceptionToAllure($error->thrownException(), $cest->getTestMethod()); } } } - // Reset Session and Cookies after all Test Runs, workaround due to functional.suite.yml restart: true - $this->getDriver()->_runAfter($e->getTest()); - } - /** - * Runs cest's after block, if necessary. - * @param \Symfony\Component\EventDispatcher\Event $e - * @param \Codeception\TestInterface $cest - * @return void - */ - private function runAfterBlock($e, $cest) - { - try { - $actorClass = $e->getTest()->getMetadata()->getCurrent('actor'); - $I = new $actorClass($cest->getScenario()); - if (version_compare(\Codeception\Codecept::VERSION, TestContextExtension::CODECEPT_AFTER_VERSION, "<=")) { - call_user_func(\Closure::bind( - function () use ($cest, $I) { - $cest->executeHook($I, 'after'); - }, - null, - $cest - )); + // check for failures in all test hooks and attach in allure + if (!empty($testResultObject->failures())) { + foreach ($testResultObject->failures() as $failure) { + if ($failure->failedTest()->getTestMethod() == $cest->getTestMethod()) { + $this->attachExceptionToAllure($failure->thrownException(), $cest->getTestMethod()); + } } - } catch (\Exception $e) { - // Do not rethrow Exception } + // Reset Session and Cookies after all Test Runs, workaround due to functional.suite.yml restart: true + $this->getDriver()->_runAfter($e->getTest()); } /** @@ -150,6 +117,46 @@ public function extractContext($trace, $class) return null; } + /** + * Attach stack trace of exceptions thrown in each test hook to allure. + * @param \Exception $exception + * @param string $testMethod + * @return mixed + */ + public function attachExceptionToAllure($exception, $testMethod) + { + if (is_subclass_of($exception, \PHPUnit\Framework\Exception::class)) { + $trace = $exception->getSerializableTrace(); + } else { + $trace = $exception->getTrace(); + } + + $context = $this->extractContext($trace, $testMethod); + + if (isset(self::TEST_HOOKS[$context])) { + $context = self::TEST_HOOKS[$context]; + } else { + $context = 'TestMethod'; + } + + AllureHelper::addAttachmentToCurrentStep($exception, $context . 'Exception'); + + //pop suppressed exceptions and attach to allure + $change = function () { + if ($this instanceof \PHPUnit\Framework\ExceptionWrapper) { + return $this->previous; + } else { + return $this->getPrevious(); + } + }; + + $previousException = $change->call($exception); + + if ($previousException !== null) { + $this->attachExceptionToAllure($previousException, $testMethod); + } + } + /** * Codeception event listener function, triggered before step. * Check if it's a new page.