001/* 002 * Copyright 2002-2015 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.support; 018 019import java.lang.reflect.Method; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.context.ApplicationContext; 025import org.springframework.core.annotation.AnnotatedElementUtils; 026import org.springframework.test.annotation.DirtiesContext; 027import org.springframework.test.annotation.DirtiesContext.ClassMode; 028import org.springframework.test.annotation.DirtiesContext.HierarchyMode; 029import org.springframework.test.annotation.DirtiesContext.MethodMode; 030import org.springframework.test.context.TestContext; 031import org.springframework.util.Assert; 032 033/** 034 * Abstract base class for {@code TestExecutionListener} implementations that 035 * provide support for marking the {@code ApplicationContext} associated with 036 * a test as <em>dirty</em> for both test classes and test methods annotated 037 * with the {@link DirtiesContext @DirtiesContext} annotation. 038 * 039 * <p>The core functionality for this class was extracted from 040 * {@link DirtiesContextTestExecutionListener} in Spring Framework 4.2. 041 * 042 * @author Sam Brannen 043 * @author Juergen Hoeller 044 * @since 4.2 045 * @see DirtiesContext 046 */ 047public abstract class AbstractDirtiesContextTestExecutionListener extends AbstractTestExecutionListener { 048 049 private static final Log logger = LogFactory.getLog(AbstractDirtiesContextTestExecutionListener.class); 050 051 052 @Override 053 public abstract int getOrder(); 054 055 /** 056 * Mark the {@linkplain ApplicationContext application context} of the supplied 057 * {@linkplain TestContext test context} as 058 * {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty} 059 * and set {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE 060 * REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}. 061 * @param testContext the test context whose application context should 062 * be marked as dirty 063 * @param hierarchyMode the context cache clearing mode to be applied if the 064 * context is part of a hierarchy; may be {@code null} 065 * @since 3.2.2 066 */ 067 protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) { 068 testContext.markApplicationContextDirty(hierarchyMode); 069 testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE); 070 } 071 072 /** 073 * Perform the actual work for {@link #beforeTestMethod} and {@link #afterTestMethod} 074 * by dirtying the context if appropriate (i.e., according to the required modes). 075 * @param testContext the test context whose application context should 076 * potentially be marked as dirty; never {@code null} 077 * @param requiredMethodMode the method mode required for a context to 078 * be marked dirty in the current phase; never {@code null} 079 * @param requiredClassMode the class mode required for a context to 080 * be marked dirty in the current phase; never {@code null} 081 * @throws Exception allows any exception to propagate 082 * @since 4.2 083 * @see #dirtyContext 084 */ 085 protected void beforeOrAfterTestMethod(TestContext testContext, MethodMode requiredMethodMode, 086 ClassMode requiredClassMode) throws Exception { 087 088 Assert.notNull(testContext, "TestContext must not be null"); 089 Assert.notNull(requiredMethodMode, "requiredMethodMode must not be null"); 090 Assert.notNull(requiredClassMode, "requiredClassMode must not be null"); 091 092 Class<?> testClass = testContext.getTestClass(); 093 Method testMethod = testContext.getTestMethod(); 094 Assert.notNull(testClass, "The test class of the supplied TestContext must not be null"); 095 Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null"); 096 097 DirtiesContext methodAnn = AnnotatedElementUtils.findMergedAnnotation(testMethod, DirtiesContext.class); 098 DirtiesContext classAnn = AnnotatedElementUtils.findMergedAnnotation(testClass, DirtiesContext.class); 099 boolean methodAnnotated = (methodAnn != null); 100 boolean classAnnotated = (classAnn != null); 101 MethodMode methodMode = (methodAnnotated ? methodAnn.methodMode() : null); 102 ClassMode classMode = (classAnnotated ? classAnn.classMode() : null); 103 104 if (logger.isDebugEnabled()) { 105 String phase = (requiredClassMode.name().startsWith("BEFORE") ? "Before" : "After"); 106 logger.debug(String.format("%s test method: context %s, class annotated with @DirtiesContext [%s] " 107 + "with mode [%s], method annotated with @DirtiesContext [%s] with mode [%s].", phase, testContext, 108 classAnnotated, classMode, methodAnnotated, methodMode)); 109 } 110 111 if ((methodMode == requiredMethodMode) || (classMode == requiredClassMode)) { 112 HierarchyMode hierarchyMode = (methodAnnotated ? methodAnn.hierarchyMode() : classAnn.hierarchyMode()); 113 dirtyContext(testContext, hierarchyMode); 114 } 115 } 116 117 /** 118 * Perform the actual work for {@link #beforeTestClass} and {@link #afterTestClass} 119 * by dirtying the context if appropriate (i.e., according to the required mode). 120 * @param testContext the test context whose application context should 121 * potentially be marked as dirty; never {@code null} 122 * @param requiredClassMode the class mode required for a context to 123 * be marked dirty in the current phase; never {@code null} 124 * @throws Exception allows any exception to propagate 125 * @since 4.2 126 * @see #dirtyContext 127 */ 128 protected void beforeOrAfterTestClass(TestContext testContext, ClassMode requiredClassMode) throws Exception { 129 Assert.notNull(testContext, "TestContext must not be null"); 130 Assert.notNull(requiredClassMode, "requiredClassMode must not be null"); 131 132 Class<?> testClass = testContext.getTestClass(); 133 Assert.notNull(testClass, "The test class of the supplied TestContext must not be null"); 134 135 DirtiesContext dirtiesContext = AnnotatedElementUtils.findMergedAnnotation(testClass, DirtiesContext.class); 136 boolean classAnnotated = (dirtiesContext != null); 137 ClassMode classMode = (classAnnotated ? dirtiesContext.classMode() : null); 138 139 if (logger.isDebugEnabled()) { 140 String phase = (requiredClassMode.name().startsWith("BEFORE") ? "Before" : "After"); 141 logger.debug(String.format( 142 "%s test class: context %s, class annotated with @DirtiesContext [%s] with mode [%s].", phase, 143 testContext, classAnnotated, classMode)); 144 } 145 146 if (classMode == requiredClassMode) { 147 dirtyContext(testContext, dirtiesContext.hierarchyMode()); 148 } 149 } 150 151}