@@ -56,7 +56,7 @@ final public function runAsyncTest(...$args)
5656 $ start = \microtime (true );
5757
5858 Loop::run (function () use (&$ returnValue , &$ exception , &$ invoked , $ args ) {
59- $ promise = $ this ->call ([ $ this , $ this -> realTestName ], ... $ args );
59+ $ promise = new Coroutine ( $ this ->runAsyncTestCycle ( $ args) );
6060 $ promise ->onResolve (function ($ error , $ value ) use (&$ invoked , &$ exception , &$ returnValue ) {
6161 $ invoked = true ;
6262 $ exception = $ error ;
@@ -91,12 +91,63 @@ final public function runAsyncTest(...$args)
9191 return $ returnValue ;
9292 }
9393
94+ private function runAsyncTestCycle (array $ args ): \Generator
95+ {
96+ try {
97+ yield $ this ->call (\Closure::fromCallable ([$ this , 'setUpAsync ' ]));
98+ } catch (\Throwable $ exception ) {
99+ throw new \Error (\sprintf (
100+ '%s::setUpAsync() failed ' ,
101+ \str_replace ("\0" , '@ ' , \get_class ($ this )) // replace NUL-byte in anonymous class name
102+ ), 0 , $ exception );
103+ }
104+
105+ try {
106+ $ returnValue = yield $ this ->call ([$ this , $ this ->realTestName ], ...$ args );
107+ } catch (\Throwable $ testException ) {
108+ // Exception rethrown in finally block below
109+ }
110+
111+ try {
112+ yield $ this ->call (\Closure::fromCallable ([$ this , 'tearDownAsync ' ]));
113+ } catch (\Throwable $ exception ) {
114+ throw new \Error (\sprintf (
115+ '%s::tearDownAsync() failed ' ,
116+ \str_replace ("\0" , '@ ' , \get_class ($ this )) // replace NUL-byte in anonymous class name
117+ ), 0 , $ exception );
118+ } finally {
119+ if (isset ($ testException )) {
120+ throw $ testException ;
121+ }
122+ }
123+
124+ return $ returnValue ;
125+ }
126+
94127 final protected function runTest ()
95128 {
96129 parent ::setName ('runAsyncTest ' );
97130 return parent ::runTest ();
98131 }
99132
133+ /**
134+ * Called before each test. Similar to {@see TestCase::setUp()}, except the method may return a promise or
135+ * coroutine (@see \Amp\call()} that will be awaited before executing the test.
136+ */
137+ protected function setUpAsync ()
138+ {
139+ // Empty method to be overloaded by inheriting class if desired.
140+ }
141+
142+ /**
143+ * Called after each test. Similar to {@see TestCase::tearDown()}, except the method may return a promise or
144+ * coroutine (@see \Amp\call()} that will be awaited before executing the next test.
145+ */
146+ protected function tearDownAsync ()
147+ {
148+ // Empty method to be overloaded by inheriting class if desired.
149+ }
150+
100151 /**
101152 * Fails the test if the loop does not run for at least the given amount of time.
102153 *
0 commit comments