001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.test;
018:
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Modifier;
021: import java.util.LinkedList;
022:
023: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
024: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
025:
026: /**
027: * Convenient superclass for tests depending on a Spring context.
028: * The test instance itself is populated by Dependency Injection.
029: *
030: * <p>Really for integration testing, not unit testing.
031: * You should <i>not</i> normally use the Spring container
032: * for unit tests: simply populate your POJOs in plain JUnit tests!
033: *
034: * <p>This supports two modes of populating the test:
035: * <ul>
036: * <li>Via Setter Dependency Injection. Simply express dependencies on objects
037: * in the test fixture, and they will be satisfied by autowiring by type.
038: * <li>Via Field Injection. Declare protected variables of the required type
039: * which match named beans in the context. This is autowire by name,
040: * rather than type. This approach is based on an approach originated by
041: * Ara Abrahmian. Setter Dependency Injection is the default: set the
042: * "populateProtectedVariables" property to true in the constructor to switch
043: * on Field Injection.
044: * </ul>
045: *
046: * @author Rod Johnson
047: * @author Rob Harrop
048: * @author Rick Evans
049: * @since 1.1.1
050: * @see #setDirty
051: * @see #contextKey
052: * @see #getContext
053: * @see #getConfigLocations
054: */
055: public abstract class AbstractDependencyInjectionSpringContextTests
056: extends AbstractSingleSpringContextTests {
057:
058: /**
059: * Constant that indicates no autowiring at all.
060: * @see #setAutowireMode
061: */
062: public static final int AUTOWIRE_NO = 0;
063:
064: /**
065: * Constant that indicates autowiring bean properties by name.
066: * @see #setAutowireMode
067: */
068: public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
069:
070: /**
071: * Constant that indicates autowiring bean properties by type.
072: * @see #setAutowireMode
073: */
074: public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
075:
076: private boolean populateProtectedVariables = false;
077:
078: private int autowireMode = AUTOWIRE_BY_TYPE;
079:
080: private boolean dependencyCheck = true;
081:
082: private String[] managedVariableNames;
083:
084: /**
085: * Default constructor for AbstractDependencyInjectionSpringContextTests.
086: */
087: public AbstractDependencyInjectionSpringContextTests() {
088: }
089:
090: /**
091: * Constructor for AbstractDependencyInjectionSpringContextTests with a JUnit name.
092: * @param name the name of this text fixture
093: */
094: public AbstractDependencyInjectionSpringContextTests(String name) {
095: super (name);
096: }
097:
098: /**
099: * Set whether to populate protected variables of this test case.
100: * Default is "false".
101: */
102: public final void setPopulateProtectedVariables(
103: boolean populateFields) {
104: this .populateProtectedVariables = populateFields;
105: }
106:
107: /**
108: * Return whether to populate protected variables of this test case.
109: */
110: public final boolean isPopulateProtectedVariables() {
111: return this .populateProtectedVariables;
112: }
113:
114: /**
115: * Set the autowire mode for test properties set by Dependency Injection.
116: * <p>The default is "AUTOWIRE_BY_TYPE". Can be set to "AUTOWIRE_BY_NAME"
117: * or "AUTOWIRE_NO" instead.
118: * @see #AUTOWIRE_BY_TYPE
119: * @see #AUTOWIRE_BY_NAME
120: * @see #AUTOWIRE_NO
121: */
122: public final void setAutowireMode(int autowireMode) {
123: this .autowireMode = autowireMode;
124: }
125:
126: /**
127: * Return the autowire mode for test properties set by Dependency Injection.
128: */
129: public final int getAutowireMode() {
130: return this .autowireMode;
131: }
132:
133: /**
134: * Set whether or not dependency checking should be performed
135: * for test properties set by Dependency Injection.
136: * <p>The default is "true", meaning that tests cannot be run
137: * unless all properties are populated.
138: */
139: public final void setDependencyCheck(boolean dependencyCheck) {
140: this .dependencyCheck = dependencyCheck;
141: }
142:
143: /**
144: * Return whether or not dependency checking should be performed
145: * for test properties set by Dependency Injection.
146: */
147: public final boolean isDependencyCheck() {
148: return this .dependencyCheck;
149: }
150:
151: /**
152: * Prepare this test instance, injecting dependencies into its
153: * protected fields and its bean properties.
154: */
155: protected void prepareTestInstance() throws Exception {
156: injectDependencies();
157: }
158:
159: /**
160: * Inject dependencies into 'this' instance (that is, this test instance).
161: * <p>The default implementation populates protected variables if the
162: * {@link #populateProtectedVariables() appropriate flag is set}, else
163: * uses autowiring if autowiring is switched on (which it is by default).
164: * <p>Override this method if you need full control over how
165: * dependencies are injected into the test instance.
166: * @throws Exception in case of dependency injection failure
167: * @see #populateProtectedVariables()
168: */
169: protected void injectDependencies() throws Exception {
170: if (isPopulateProtectedVariables()) {
171: if (this .managedVariableNames == null) {
172: initManagedVariableNames();
173: }
174: populateProtectedVariables();
175: } else if (getAutowireMode() != AUTOWIRE_NO) {
176: getApplicationContext().getBeanFactory()
177: .autowireBeanProperties(this , getAutowireMode(),
178: isDependencyCheck());
179: }
180: }
181:
182: private void initManagedVariableNames()
183: throws IllegalAccessException {
184: LinkedList managedVarNames = new LinkedList();
185: Class clazz = getClass();
186:
187: do {
188: Field[] fields = clazz.getDeclaredFields();
189: if (logger.isDebugEnabled()) {
190: logger.debug("Found " + fields.length + " fields on "
191: + clazz);
192: }
193:
194: for (int i = 0; i < fields.length; i++) {
195: Field field = fields[i];
196: field.setAccessible(true);
197: if (logger.isDebugEnabled()) {
198: logger.debug("Candidate field: " + field);
199: }
200: if (isProtectedInstanceField(field)) {
201: Object oldValue = field.get(this );
202: if (oldValue == null) {
203: managedVarNames.add(field.getName());
204: if (logger.isDebugEnabled()) {
205: logger.debug("Added managed variable '"
206: + field.getName() + "'");
207: }
208: } else {
209: if (logger.isDebugEnabled()) {
210: logger.debug("Rejected managed variable '"
211: + field.getName() + "'");
212: }
213: }
214: }
215: }
216: clazz = clazz.getSuperclass();
217: } while (!clazz
218: .equals(AbstractDependencyInjectionSpringContextTests.class));
219:
220: this .managedVariableNames = (String[]) managedVarNames
221: .toArray(new String[managedVarNames.size()]);
222: }
223:
224: private boolean isProtectedInstanceField(Field field) {
225: int modifiers = field.getModifiers();
226: return !Modifier.isStatic(modifiers)
227: && Modifier.isProtected(modifiers);
228: }
229:
230: private void populateProtectedVariables()
231: throws IllegalAccessException {
232: for (int i = 0; i < this .managedVariableNames.length; i++) {
233: String varName = this .managedVariableNames[i];
234: Object bean = null;
235: try {
236: Field field = findField(getClass(), varName);
237: bean = getApplicationContext().getBean(varName,
238: field.getType());
239: field.setAccessible(true);
240: field.set(this , bean);
241: if (logger.isDebugEnabled()) {
242: logger.debug("Populated field: " + field);
243: }
244: } catch (NoSuchFieldException ex) {
245: if (logger.isWarnEnabled()) {
246: logger.warn("No field with name '" + varName + "'");
247: }
248: } catch (NoSuchBeanDefinitionException ex) {
249: if (logger.isWarnEnabled()) {
250: logger.warn("No bean with name '" + varName + "'");
251: }
252: }
253: }
254: }
255:
256: private Field findField(Class clazz, String name)
257: throws NoSuchFieldException {
258: try {
259: return clazz.getDeclaredField(name);
260: } catch (NoSuchFieldException ex) {
261: Class super class = clazz.getSuperclass();
262: if (super class != AbstractSpringContextTests.class) {
263: return findField(superclass, name);
264: } else {
265: throw ex;
266: }
267: }
268: }
269:
270: }
|