13
13
/**
14
14
* Class TestContextExtension
15
15
* @SuppressWarnings(PHPMD.UnusedPrivateField)
16
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
16
17
*/
17
18
class TestContextExtension extends BaseExtension
18
19
{
19
20
const TEST_PHASE_AFTER = "_after " ;
20
- const CODECEPT_AFTER_VERSION = "2.3.9 " ;
21
+ const TEST_PHASE_BEFORE = "_before " ;
22
+
21
23
const TEST_FAILED_FILE = 'failed ' ;
24
+ const TEST_HOOKS = [
25
+ self ::TEST_PHASE_AFTER => 'AfterHook ' ,
26
+ self ::TEST_PHASE_BEFORE => 'BeforeHook '
27
+ ];
22
28
23
29
/**
24
30
* Codeception Events Mapping to methods
@@ -36,7 +42,6 @@ public function _initialize()
36
42
{
37
43
$ events = [
38
44
Events::TEST_START => 'testStart ' ,
39
- Events::TEST_FAIL => 'testFail ' ,
40
45
Events::STEP_AFTER => 'afterStep ' ,
41
46
Events::TEST_END => 'testEnd ' ,
42
47
Events::RESULT_PRINT_AFTER => 'saveFailed '
@@ -57,23 +62,7 @@ public function testStart()
57
62
}
58
63
59
64
/**
60
- * Codeception event listener function, triggered on test failure.
61
- * @param \Codeception\Event\FailEvent $e
62
- * @return void
63
- */
64
- public function testFail (\Codeception \Event \FailEvent $ e )
65
- {
66
- $ cest = $ e ->getTest ();
67
- $ context = $ this ->extractContext ($ e ->getFail ()->getTrace (), $ cest ->getTestMethod ());
68
- // Do not attempt to run _after if failure was in the _after block
69
- // Try to run _after but catch exceptions to prevent them from overwriting original failure.
70
- if ($ context != TestContextExtension::TEST_PHASE_AFTER ) {
71
- $ this ->runAfterBlock ($ e , $ cest );
72
- }
73
- }
74
-
75
- /**
76
- * Codeception event listener function, triggered on test ending (naturally or by error).
65
+ * Codeception event listener function, triggered on test ending naturally or by errors/failures.
77
66
* @param \Codeception\Event\TestEvent $e
78
67
* @return void
79
68
* @throws \Exception
@@ -82,55 +71,33 @@ public function testEnd(\Codeception\Event\TestEvent $e)
82
71
{
83
72
$ cest = $ e ->getTest ();
84
73
85
- //Access private TestResultObject to find stack and if there are any errors (as opposed to failures)
74
+ //Access private TestResultObject to find stack and if there are any errors/ failures
86
75
$ testResultObject = call_user_func (\Closure::bind (
87
76
function () use ($ cest ) {
88
77
return $ cest ->getTestResultObject ();
89
78
},
90
79
$ cest
91
80
));
92
- $ errors = $ testResultObject ->errors ();
93
- if (!empty ($ errors )) {
94
- foreach ($ errors as $ error ) {
95
- if ($ error ->failedTest ()->getTestMethod () == $ cest ->getName ()) {
96
- $ stack = $ errors [0 ]->thrownException ()->getTrace ();
97
- $ context = $ this ->extractContext ($ stack , $ cest ->getTestMethod ());
98
- // Do not attempt to run _after if failure was in the _after block
99
- // Try to run _after but catch exceptions to prevent them from overwriting original failure.
100
- if ($ context != TestContextExtension::TEST_PHASE_AFTER ) {
101
- $ this ->runAfterBlock ($ e , $ cest );
102
- }
103
- continue ;
81
+
82
+ // check for errors in all test hooks and attach in allure
83
+ if (!empty ($ testResultObject ->errors ())) {
84
+ foreach ($ testResultObject ->errors () as $ error ) {
85
+ if ($ error ->failedTest ()->getTestMethod () == $ cest ->getTestMethod ()) {
86
+ $ this ->attachExceptionToAllure ($ error ->thrownException (), $ cest ->getTestMethod ());
104
87
}
105
88
}
106
89
}
107
- // Reset Session and Cookies after all Test Runs, workaround due to functional.suite.yml restart: true
108
- $ this ->getDriver ()->_runAfter ($ e ->getTest ());
109
- }
110
90
111
- /**
112
- * Runs cest's after block, if necessary.
113
- * @param \Symfony\Component\EventDispatcher\Event $e
114
- * @param \Codeception\TestInterface $cest
115
- * @return void
116
- */
117
- private function runAfterBlock ($ e , $ cest )
118
- {
119
- try {
120
- $ actorClass = $ e ->getTest ()->getMetadata ()->getCurrent ('actor ' );
121
- $ I = new $ actorClass ($ cest ->getScenario ());
122
- if (version_compare (\Codeception \Codecept::VERSION , TestContextExtension::CODECEPT_AFTER_VERSION , "<= " )) {
123
- call_user_func (\Closure::bind (
124
- function () use ($ cest , $ I ) {
125
- $ cest ->executeHook ($ I , 'after ' );
126
- },
127
- null ,
128
- $ cest
129
- ));
91
+ // check for failures in all test hooks and attach in allure
92
+ if (!empty ($ testResultObject ->failures ())) {
93
+ foreach ($ testResultObject ->failures () as $ failure ) {
94
+ if ($ failure ->failedTest ()->getTestMethod () == $ cest ->getTestMethod ()) {
95
+ $ this ->attachExceptionToAllure ($ failure ->thrownException (), $ cest ->getTestMethod ());
96
+ }
130
97
}
131
- } catch (\Exception $ e ) {
132
- // Do not rethrow Exception
133
98
}
99
+ // Reset Session and Cookies after all Test Runs, workaround due to functional.suite.yml restart: true
100
+ $ this ->getDriver ()->_runAfter ($ e ->getTest ());
134
101
}
135
102
136
103
/**
@@ -150,6 +117,46 @@ public function extractContext($trace, $class)
150
117
return null ;
151
118
}
152
119
120
+ /**
121
+ * Attach stack trace of exceptions thrown in each test hook to allure.
122
+ * @param \Exception $exception
123
+ * @param string $testMethod
124
+ * @return mixed
125
+ */
126
+ public function attachExceptionToAllure ($ exception , $ testMethod )
127
+ {
128
+ if (is_subclass_of ($ exception , \PHPUnit \Framework \Exception::class)) {
129
+ $ trace = $ exception ->getSerializableTrace ();
130
+ } else {
131
+ $ trace = $ exception ->getTrace ();
132
+ }
133
+
134
+ $ context = $ this ->extractContext ($ trace , $ testMethod );
135
+
136
+ if (isset (self ::TEST_HOOKS [$ context ])) {
137
+ $ context = self ::TEST_HOOKS [$ context ];
138
+ } else {
139
+ $ context = 'TestMethod ' ;
140
+ }
141
+
142
+ AllureHelper::addAttachmentToCurrentStep ($ exception , $ context . 'Exception ' );
143
+
144
+ //pop suppressed exceptions and attach to allure
145
+ $ change = function () {
146
+ if ($ this instanceof \PHPUnit \Framework \ExceptionWrapper) {
147
+ return $ this ->previous ;
148
+ } else {
149
+ return $ this ->getPrevious ();
150
+ }
151
+ };
152
+
153
+ $ previousException = $ change ->call ($ exception );
154
+
155
+ if ($ previousException !== null ) {
156
+ $ this ->attachExceptionToAllure ($ previousException , $ testMethod );
157
+ }
158
+ }
159
+
153
160
/**
154
161
* Codeception event listener function, triggered before step.
155
162
* Check if it's a new page.
0 commit comments