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 — 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} — 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} — 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 — 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}