001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.context;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Method;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.List;
024import java.util.function.Supplier;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028
029import org.springframework.lang.Nullable;
030import org.springframework.util.ClassUtils;
031import org.springframework.util.ReflectionUtils;
032
033/**
034 * {@code TestContextManager} is the main entry point into the <em>Spring
035 * TestContext Framework</em>.
036 *
037 * <p>Specifically, a {@code TestContextManager} is responsible for managing a
038 * single {@link TestContext} and signaling events to each registered
039 * {@link TestExecutionListener} at the following test execution points.
040 *
041 * <ul>
042 * <li>{@link #beforeTestClass() before test class execution}: prior to any
043 * <em>before class callbacks</em> of a particular testing framework (e.g.,
044 * JUnit 4's {@link org.junit.BeforeClass @BeforeClass})</li>
045 * <li>{@link #prepareTestInstance test instance preparation}:
046 * immediately following instantiation of the test class</li>
047 * <li>{@link #beforeTestMethod before test setup}:
048 * prior to any <em>before method callbacks</em> of a particular testing framework
049 * (e.g., JUnit 4's {@link org.junit.Before @Before})</li>
050 * <li>{@link #beforeTestExecution before test execution}:
051 * immediately before execution of the {@linkplain java.lang.reflect.Method
052 * test method} but after test setup</li>
053 * <li>{@link #afterTestExecution after test execution}:
054 * immediately after execution of the {@linkplain java.lang.reflect.Method
055 * test method} but before test tear down</li>
056 * <li>{@link #afterTestMethod(Object, Method, Throwable) after test tear down}:
057 * after any <em>after method callbacks</em> of a particular testing
058 * framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
059 * <li>{@link #afterTestClass() after test class execution}: after any
060 * <em>after class callbacks</em> of a particular testing framework (e.g., JUnit 4's
061 * {@link org.junit.AfterClass @AfterClass})</li>
062 * </ul>
063 *
064 * <p>Support for loading and accessing
065 * {@linkplain org.springframework.context.ApplicationContext application contexts},
066 * dependency injection of test instances,
067 * {@linkplain org.springframework.transaction.annotation.Transactional transactional}
068 * execution of test methods, etc. is provided by
069 * {@link SmartContextLoader ContextLoaders} and {@code TestExecutionListeners},
070 * which are configured via {@link ContextConfiguration @ContextConfiguration} and
071 * {@link TestExecutionListeners @TestExecutionListeners}, respectively.
072 *
073 * <p>Bootstrapping of the {@code TestContext}, the default {@code ContextLoader},
074 * default {@code TestExecutionListeners}, and their collaborators is performed
075 * by a {@link TestContextBootstrapper}, which is configured via
076 * {@link BootstrapWith @BootstrapWith}.
077 *
078 * @author Sam Brannen
079 * @author Juergen Hoeller
080 * @since 2.5
081 * @see BootstrapWith
082 * @see BootstrapContext
083 * @see TestContextBootstrapper
084 * @see TestContext
085 * @see TestExecutionListener
086 * @see TestExecutionListeners
087 * @see ContextConfiguration
088 * @see ContextHierarchy
089 */
090public class TestContextManager {
091
092        private static final Log logger = LogFactory.getLog(TestContextManager.class);
093
094        private final TestContext testContext;
095
096        private final ThreadLocal<TestContext> testContextHolder = ThreadLocal.withInitial(
097                        // Implemented as an anonymous inner class instead of a lambda expression due to a bug
098                        // in Eclipse IDE: "The blank final field testContext may not have been initialized"
099                        new Supplier<TestContext>() {
100                                @Override
101                                public TestContext get() {
102                                        return copyTestContext(TestContextManager.this.testContext);
103                                }
104                        });
105
106        private final List<TestExecutionListener> testExecutionListeners = new ArrayList<>();
107
108
109        /**
110         * Construct a new {@code TestContextManager} for the supplied {@linkplain Class test class}.
111         * <p>Delegates to {@link #TestContextManager(TestContextBootstrapper)} with
112         * the {@link TestContextBootstrapper} configured for the test class. If the
113         * {@link BootstrapWith @BootstrapWith} annotation is present on the test
114         * class, either directly or as a meta-annotation, then its
115         * {@link BootstrapWith#value value} will be used as the bootstrapper type;
116         * otherwise, the {@link org.springframework.test.context.support.DefaultTestContextBootstrapper
117         * DefaultTestContextBootstrapper} will be used.
118         * @param testClass the test class to be managed
119         * @see #TestContextManager(TestContextBootstrapper)
120         */
121        public TestContextManager(Class<?> testClass) {
122                this(BootstrapUtils.resolveTestContextBootstrapper(BootstrapUtils.createBootstrapContext(testClass)));
123        }
124
125        /**
126         * Construct a new {@code TestContextManager} using the supplied {@link TestContextBootstrapper}
127         * and {@linkplain #registerTestExecutionListeners register} the necessary
128         * {@link TestExecutionListener TestExecutionListeners}.
129         * <p>Delegates to the supplied {@code TestContextBootstrapper} for building
130         * the {@code TestContext} and retrieving the {@code TestExecutionListeners}.
131         * @param testContextBootstrapper the bootstrapper to use
132         * @see TestContextBootstrapper#buildTestContext
133         * @see TestContextBootstrapper#getTestExecutionListeners
134         * @see #registerTestExecutionListeners
135         */
136        public TestContextManager(TestContextBootstrapper testContextBootstrapper) {
137                this.testContext = testContextBootstrapper.buildTestContext();
138                registerTestExecutionListeners(testContextBootstrapper.getTestExecutionListeners());
139        }
140
141        /**
142         * Get the {@link TestContext} managed by this {@code TestContextManager}.
143         */
144        public final TestContext getTestContext() {
145                return this.testContextHolder.get();
146        }
147
148        /**
149         * Register the supplied list of {@link TestExecutionListener TestExecutionListeners}
150         * by appending them to the list of listeners used by this {@code TestContextManager}.
151         * @see #registerTestExecutionListeners(TestExecutionListener...)
152         */
153        public void registerTestExecutionListeners(List<TestExecutionListener> testExecutionListeners) {
154                registerTestExecutionListeners(testExecutionListeners.toArray(new TestExecutionListener[0]));
155        }
156
157        /**
158         * Register the supplied array of {@link TestExecutionListener TestExecutionListeners}
159         * by appending them to the list of listeners used by this {@code TestContextManager}.
160         */
161        public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) {
162                for (TestExecutionListener listener : testExecutionListeners) {
163                        if (logger.isTraceEnabled()) {
164                                logger.trace("Registering TestExecutionListener: " + listener);
165                        }
166                        this.testExecutionListeners.add(listener);
167                }
168        }
169
170        /**
171         * Get the current {@link TestExecutionListener TestExecutionListeners}
172         * registered for this {@code TestContextManager}.
173         * <p>Allows for modifications, e.g. adding a listener to the beginning of the list.
174         * However, make sure to keep the list stable while actually executing tests.
175         */
176        public final List<TestExecutionListener> getTestExecutionListeners() {
177                return this.testExecutionListeners;
178        }
179
180        /**
181         * Get a copy of the {@link TestExecutionListener TestExecutionListeners}
182         * registered for this {@code TestContextManager} in reverse order.
183         */
184        private List<TestExecutionListener> getReversedTestExecutionListeners() {
185                List<TestExecutionListener> listenersReversed = new ArrayList<>(getTestExecutionListeners());
186                Collections.reverse(listenersReversed);
187                return listenersReversed;
188        }
189
190        /**
191         * Hook for pre-processing a test class <em>before</em> execution of any
192         * tests within the class. Should be called prior to any framework-specific
193         * <em>before class methods</em> (e.g., methods annotated with JUnit 4's
194         * {@link org.junit.BeforeClass @BeforeClass}).
195         * <p>An attempt will be made to give each registered
196         * {@link TestExecutionListener} a chance to pre-process the test class
197         * execution. If a listener throws an exception, however, the remaining
198         * registered listeners will <strong>not</strong> be called.
199         * @throws Exception if a registered TestExecutionListener throws an
200         * exception
201         * @since 3.0
202         * @see #getTestExecutionListeners()
203         */
204        public void beforeTestClass() throws Exception {
205                Class<?> testClass = getTestContext().getTestClass();
206                if (logger.isTraceEnabled()) {
207                        logger.trace("beforeTestClass(): class [" + testClass.getName() + "]");
208                }
209                getTestContext().updateState(null, null, null);
210
211                for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
212                        try {
213                                testExecutionListener.beforeTestClass(getTestContext());
214                        }
215                        catch (Throwable ex) {
216                                logException(ex, "beforeTestClass", testExecutionListener, testClass);
217                                ReflectionUtils.rethrowException(ex);
218                        }
219                }
220        }
221
222        /**
223         * Hook for preparing a test instance prior to execution of any individual
224         * test methods, for example for injecting dependencies, etc. Should be
225         * called immediately after instantiation of the test instance.
226         * <p>The managed {@link TestContext} will be updated with the supplied
227         * {@code testInstance}.
228         * <p>An attempt will be made to give each registered
229         * {@link TestExecutionListener} a chance to prepare the test instance. If a
230         * listener throws an exception, however, the remaining registered listeners
231         * will <strong>not</strong> be called.
232         * @param testInstance the test instance to prepare (never {@code null})
233         * @throws Exception if a registered TestExecutionListener throws an exception
234         * @see #getTestExecutionListeners()
235         */
236        public void prepareTestInstance(Object testInstance) throws Exception {
237                if (logger.isTraceEnabled()) {
238                        logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
239                }
240                getTestContext().updateState(testInstance, null, null);
241
242                for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
243                        try {
244                                testExecutionListener.prepareTestInstance(getTestContext());
245                        }
246                        catch (Throwable ex) {
247                                if (logger.isErrorEnabled()) {
248                                        logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
249                                                        "] to prepare test instance [" + testInstance + "]", ex);
250                                }
251                                ReflectionUtils.rethrowException(ex);
252                        }
253                }
254        }
255
256        /**
257         * Hook for pre-processing a test <em>before</em> execution of <em>before</em>
258         * lifecycle callbacks of the underlying test framework &mdash; for example,
259         * setting up test fixtures, starting a transaction, etc.
260         * <p>This method <strong>must</strong> be called immediately prior to
261         * framework-specific <em>before</em> lifecycle callbacks (e.g., methods
262         * annotated with JUnit 4's {@link org.junit.Before @Before}). For historical
263         * reasons, this method is named {@code beforeTestMethod}. Since the
264         * introduction of {@link #beforeTestExecution}, a more suitable name for
265         * this method might be something like {@code beforeTestSetUp} or
266         * {@code beforeEach}; however, it is unfortunately impossible to rename
267         * this method due to backward compatibility concerns.
268         * <p>The managed {@link TestContext} will be updated with the supplied
269         * {@code testInstance} and {@code testMethod}.
270         * <p>An attempt will be made to give each registered
271         * {@link TestExecutionListener} a chance to perform its pre-processing.
272         * If a listener throws an exception, however, the remaining registered
273         * listeners will <strong>not</strong> be called.
274         * @param testInstance the current test instance (never {@code null})
275         * @param testMethod the test method which is about to be executed on the
276         * test instance
277         * @throws Exception if a registered TestExecutionListener throws an exception
278         * @see #afterTestMethod
279         * @see #beforeTestExecution
280         * @see #afterTestExecution
281         * @see #getTestExecutionListeners()
282         */
283        public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
284                String callbackName = "beforeTestMethod";
285                prepareForBeforeCallback(callbackName, testInstance, testMethod);
286
287                for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
288                        try {
289                                testExecutionListener.beforeTestMethod(getTestContext());
290                        }
291                        catch (Throwable ex) {
292                                handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod);
293                        }
294                }
295        }
296
297        /**
298         * Hook for pre-processing a test <em>immediately before</em> execution of
299         * the {@linkplain java.lang.reflect.Method test method} in the supplied
300         * {@linkplain TestContext test context} &mdash; for example, for timing
301         * or logging purposes.
302         * <p>This method <strong>must</strong> be called after framework-specific
303         * <em>before</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
304         * {@link org.junit.Before @Before}).
305         * <p>The managed {@link TestContext} will be updated with the supplied
306         * {@code testInstance} and {@code testMethod}.
307         * <p>An attempt will be made to give each registered
308         * {@link TestExecutionListener} a chance to perform its pre-processing.
309         * If a listener throws an exception, however, the remaining registered
310         * listeners will <strong>not</strong> be called.
311         * @param testInstance the current test instance (never {@code null})
312         * @param testMethod the test method which is about to be executed on the
313         * test instance
314         * @throws Exception if a registered TestExecutionListener throws an exception
315         * @since 5.0
316         * @see #beforeTestMethod
317         * @see #afterTestMethod
318         * @see #beforeTestExecution
319         * @see #afterTestExecution
320         * @see #getTestExecutionListeners()
321         */
322        public void beforeTestExecution(Object testInstance, Method testMethod) throws Exception {
323                String callbackName = "beforeTestExecution";
324                prepareForBeforeCallback(callbackName, testInstance, testMethod);
325
326                for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
327                        try {
328                                testExecutionListener.beforeTestExecution(getTestContext());
329                        }
330                        catch (Throwable ex) {
331                                handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod);
332                        }
333                }
334        }
335
336        /**
337         * Hook for post-processing a test <em>immediately after</em> execution of
338         * the {@linkplain java.lang.reflect.Method test method} in the supplied
339         * {@linkplain TestContext test context} &mdash; for example, for timing
340         * or logging purposes.
341         * <p>This method <strong>must</strong> be called before framework-specific
342         * <em>after</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
343         * {@link org.junit.After @After}).
344         * <p>The managed {@link TestContext} will be updated with the supplied
345         * {@code testInstance}, {@code testMethod}, and {@code exception}.
346         * <p>Each registered {@link TestExecutionListener} will be given a chance
347         * to perform its post-processing. If a listener throws an exception, the
348         * remaining registered listeners will still be called. After all listeners
349         * have executed, the first caught exception will be rethrown with any
350         * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
351         * the first exception.
352         * <p>Note that registered listeners will be executed in the opposite
353         * order in which they were registered.
354         * @param testInstance the current test instance (never {@code null})
355         * @param testMethod the test method which has just been executed on the
356         * test instance
357         * @param exception the exception that was thrown during execution of the
358         * test method or by a TestExecutionListener, or {@code null} if none
359         * was thrown
360         * @throws Exception if a registered TestExecutionListener throws an exception
361         * @since 5.0
362         * @see #beforeTestMethod
363         * @see #afterTestMethod
364         * @see #beforeTestExecution
365         * @see #getTestExecutionListeners()
366         * @see Throwable#addSuppressed(Throwable)
367         */
368        public void afterTestExecution(Object testInstance, Method testMethod, @Nullable Throwable exception)
369                        throws Exception {
370
371                String callbackName = "afterTestExecution";
372                prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
373                Throwable afterTestExecutionException = null;
374
375                // Traverse the TestExecutionListeners in reverse order to ensure proper
376                // "wrapper"-style execution of listeners.
377                for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
378                        try {
379                                testExecutionListener.afterTestExecution(getTestContext());
380                        }
381                        catch (Throwable ex) {
382                                logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
383                                if (afterTestExecutionException == null) {
384                                        afterTestExecutionException = ex;
385                                }
386                                else {
387                                        afterTestExecutionException.addSuppressed(ex);
388                                }
389                        }
390                }
391
392                if (afterTestExecutionException != null) {
393                        ReflectionUtils.rethrowException(afterTestExecutionException);
394                }
395        }
396
397        /**
398         * Hook for post-processing a test <em>after</em> execution of <em>after</em>
399         * lifecycle callbacks of the underlying test framework &mdash; for example,
400         * tearing down test fixtures, ending a transaction, etc.
401         * <p>This method <strong>must</strong> be called immediately after
402         * framework-specific <em>after</em> lifecycle callbacks (e.g., methods
403         * annotated with JUnit 4's {@link org.junit.After @After}). For historical
404         * reasons, this method is named {@code afterTestMethod}. Since the
405         * introduction of {@link #afterTestExecution}, a more suitable name for
406         * this method might be something like {@code afterTestTearDown} or
407         * {@code afterEach}; however, it is unfortunately impossible to rename
408         * this method due to backward compatibility concerns.
409         * <p>The managed {@link TestContext} will be updated with the supplied
410         * {@code testInstance}, {@code testMethod}, and {@code exception}.
411         * <p>Each registered {@link TestExecutionListener} will be given a chance
412         * to perform its post-processing. If a listener throws an exception, the
413         * remaining registered listeners will still be called. After all listeners
414         * have executed, the first caught exception will be rethrown with any
415         * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
416         * the first exception.
417         * <p>Note that registered listeners will be executed in the opposite
418         * @param testInstance the current test instance (never {@code null})
419         * @param testMethod the test method which has just been executed on the
420         * test instance
421         * @param exception the exception that was thrown during execution of the test
422         * method or by a TestExecutionListener, or {@code null} if none was thrown
423         * @throws Exception if a registered TestExecutionListener throws an exception
424         * @see #beforeTestMethod
425         * @see #beforeTestExecution
426         * @see #afterTestExecution
427         * @see #getTestExecutionListeners()
428         * @see Throwable#addSuppressed(Throwable)
429         */
430        public void afterTestMethod(Object testInstance, Method testMethod, @Nullable Throwable exception)
431                        throws Exception {
432
433                String callbackName = "afterTestMethod";
434                prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
435                Throwable afterTestMethodException = null;
436
437                // Traverse the TestExecutionListeners in reverse order to ensure proper
438                // "wrapper"-style execution of listeners.
439                for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
440                        try {
441                                testExecutionListener.afterTestMethod(getTestContext());
442                        }
443                        catch (Throwable ex) {
444                                logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
445                                if (afterTestMethodException == null) {
446                                        afterTestMethodException = ex;
447                                }
448                                else {
449                                        afterTestMethodException.addSuppressed(ex);
450                                }
451                        }
452                }
453
454                if (afterTestMethodException != null) {
455                        ReflectionUtils.rethrowException(afterTestMethodException);
456                }
457        }
458
459        /**
460         * Hook for post-processing a test class <em>after</em> execution of all
461         * tests within the class. Should be called after any framework-specific
462         * <em>after class methods</em> (e.g., methods annotated with JUnit 4's
463         * {@link org.junit.AfterClass @AfterClass}).
464         * <p>Each registered {@link TestExecutionListener} will be given a chance
465         * to perform its post-processing. If a listener throws an exception, the
466         * remaining registered listeners will still be called. After all listeners
467         * have executed, the first caught exception will be rethrown with any
468         * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
469         * the first exception.
470         * <p>Note that registered listeners will be executed in the opposite
471         * @throws Exception if a registered TestExecutionListener throws an exception
472         * @since 3.0
473         * @see #getTestExecutionListeners()
474         * @see Throwable#addSuppressed(Throwable)
475         */
476        public void afterTestClass() throws Exception {
477                Class<?> testClass = getTestContext().getTestClass();
478                if (logger.isTraceEnabled()) {
479                        logger.trace("afterTestClass(): class [" + testClass.getName() + "]");
480                }
481                getTestContext().updateState(null, null, null);
482
483                Throwable afterTestClassException = null;
484                // Traverse the TestExecutionListeners in reverse order to ensure proper
485                // "wrapper"-style execution of listeners.
486                for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
487                        try {
488                                testExecutionListener.afterTestClass(getTestContext());
489                        }
490                        catch (Throwable ex) {
491                                logException(ex, "afterTestClass", testExecutionListener, testClass);
492                                if (afterTestClassException == null) {
493                                        afterTestClassException = ex;
494                                }
495                                else {
496                                        afterTestClassException.addSuppressed(ex);
497                                }
498                        }
499                }
500
501                this.testContextHolder.remove();
502
503                if (afterTestClassException != null) {
504                        ReflectionUtils.rethrowException(afterTestClassException);
505                }
506        }
507
508        private void prepareForBeforeCallback(String callbackName, Object testInstance, Method testMethod) {
509                if (logger.isTraceEnabled()) {
510                        logger.trace(String.format("%s(): instance [%s], method [%s]", callbackName, testInstance, testMethod));
511                }
512                getTestContext().updateState(testInstance, testMethod, null);
513        }
514
515        private void prepareForAfterCallback(String callbackName, Object testInstance, Method testMethod,
516                        @Nullable Throwable exception) {
517
518                if (logger.isTraceEnabled()) {
519                        logger.trace(String.format("%s(): instance [%s], method [%s], exception [%s]",
520                                        callbackName, testInstance, testMethod, exception));
521                }
522                getTestContext().updateState(testInstance, testMethod, exception);
523        }
524
525        private void handleBeforeException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
526                        Object testInstance, Method testMethod) throws Exception {
527
528                logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
529                ReflectionUtils.rethrowException(ex);
530        }
531
532        private void logException(
533                        Throwable ex, String callbackName, TestExecutionListener testExecutionListener, Class<?> testClass) {
534
535                if (logger.isWarnEnabled()) {
536                        logger.warn(String.format("Caught exception while invoking '%s' callback on " +
537                                        "TestExecutionListener [%s] for test class [%s]", callbackName, testExecutionListener,
538                                        testClass), ex);
539                }
540        }
541
542        private void logException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
543                        Object testInstance, Method testMethod) {
544
545                if (logger.isWarnEnabled()) {
546                        logger.warn(String.format("Caught exception while invoking '%s' callback on " +
547                                        "TestExecutionListener [%s] for test method [%s] and test instance [%s]",
548                                        callbackName, testExecutionListener, testMethod, testInstance), ex);
549                }
550        }
551
552
553        /**
554         * Attempt to create a copy of the supplied {@code TestContext} using its
555         * <em>copy constructor</em>.
556         */
557        private static TestContext copyTestContext(TestContext testContext) {
558                Constructor<? extends TestContext> constructor =
559                                ClassUtils.getConstructorIfAvailable(testContext.getClass(), testContext.getClass());
560
561                if (constructor != null) {
562                        try {
563                                ReflectionUtils.makeAccessible(constructor);
564                                return constructor.newInstance(testContext);
565                        }
566                        catch (Exception ex) {
567                                if (logger.isInfoEnabled()) {
568                                        logger.info(String.format("Failed to invoke copy constructor for [%s]; " +
569                                                        "concurrent test execution is therefore likely not supported.",
570                                                        testContext), ex);
571                                }
572                        }
573                }
574
575                // Fallback to original instance
576                return testContext;
577        }
578
579}