@@ -23,7 +23,8 @@ class TestContextExtension extends \Codeception\Extension
23
23
*/
24
24
public static $ events = [
25
25
Events::TEST_FAIL => 'testFail ' ,
26
- Events::STEP_AFTER => 'afterStep '
26
+ Events::STEP_AFTER => 'afterStep ' ,
27
+ Events::TEST_END => 'testError '
27
28
];
28
29
29
30
/**
@@ -38,22 +39,61 @@ public function testFail(\Codeception\Event\FailEvent $e)
38
39
// Do not attempt to run _after if failure was in the _after block
39
40
// Try to run _after but catch exceptions to prevent them from overwriting original failure.
40
41
if ($ context != TestContextExtension::TEST_PHASE_AFTER ) {
41
- try {
42
- $ actorClass = $ e ->getTest ()->getMetadata ()->getCurrent ('actor ' );
43
- $ I = new $ actorClass ($ cest ->getScenario ());
44
- call_user_func (\Closure::bind (
45
- function () use ($ cest , $ I ) {
46
- $ cest ->executeHook ($ I , 'after ' );
47
- },
48
- null ,
49
- $ cest
50
- ));
51
- } catch (\Exception $ e ) {
52
- // Do not rethrow Exception
42
+ $ this ->runAfterBlock ($ e , $ cest );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Codeception event listener function, triggered on test error.
48
+ * @param \Codeception\Event\TestEvent $e
49
+ * @return void
50
+ */
51
+ public function testError (\Codeception \Event \TestEvent $ e )
52
+ {
53
+ $ cest = $ e ->getTest ();
54
+
55
+ //Access private TestResultObject to find stack and if there are any errors (as opposed to failures)
56
+ $ testResultObject = call_user_func (\Closure::bind (
57
+ function () use ($ cest ) {
58
+ return $ cest ->getTestResultObject ();
59
+ },
60
+ $ cest
61
+ ));
62
+ $ errors = $ testResultObject ->errors ();
63
+ if (!empty ($ errors )) {
64
+ $ stack = $ errors [0 ]->thrownException ()->getTrace ();
65
+ $ context = $ this ->extractContext ($ stack , $ cest ->getTestMethod ());
66
+ // Do not attempt to run _after if failure was in the _after block
67
+ // Try to run _after but catch exceptions to prevent them from overwriting original failure.
68
+ if ($ context != TestContextExtension::TEST_PHASE_AFTER ) {
69
+ $ this ->runAfterBlock ($ e , $ cest );
53
70
}
54
71
}
55
72
}
56
73
74
+ /**
75
+ * Runs cest's after block, if necessary.
76
+ * @param Symfony\Component\EventDispatcher\Event $e
77
+ * @param \Codeception\TestInterface $cest
78
+ * @return void
79
+ */
80
+ private function runAfterBlock ($ e , $ cest )
81
+ {
82
+ try {
83
+ $ actorClass = $ e ->getTest ()->getMetadata ()->getCurrent ('actor ' );
84
+ $ I = new $ actorClass ($ cest ->getScenario ());
85
+ call_user_func (\Closure::bind (
86
+ function () use ($ cest , $ I ) {
87
+ $ cest ->executeHook ($ I , 'after ' );
88
+ },
89
+ null ,
90
+ $ cest
91
+ ));
92
+ } catch (\Exception $ e ) {
93
+ // Do not rethrow Exception
94
+ }
95
+ }
96
+
57
97
/**
58
98
* Extracts hook method from trace, looking specifically for the cest class given.
59
99
* @param array $trace
0 commit comments