1 /*
2  * Copyright 2002-2009 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.test.context.testng;
18 
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.springframework.context.ApplicationContext;
25 import org.springframework.context.ApplicationContextAware;
26 import org.springframework.test.context.ContextConfiguration;
27 import org.springframework.test.context.TestContext;
28 import org.springframework.test.context.TestContextManager;
29 import org.springframework.test.context.TestExecutionListeners;
30 import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
31 import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
32 import org.testng.IHookCallBack;
33 import org.testng.IHookable;
34 import org.testng.ITestResult;
35 import org.testng.annotations.AfterClass;
36 import org.testng.annotations.AfterMethod;
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.BeforeMethod;
39 
40 /**
41  * <p>
42  * Abstract base test class which integrates the
43  * <em>Spring TestContext Framework</em> with explicit
44  * {@link ApplicationContext} testing support in a <strong>TestNG</strong>
45  * environment.
46  * </p>
47  * <p>
48  * Concrete subclasses:
49  * </p>
50  * <ul>
51  * <li>Typically declare a class-level {@link ContextConfiguration
52  * &#064;ContextConfiguration} annotation to configure the
53  * {@link ApplicationContext application context}
54  * {@link ContextConfiguration#locations() resource locations}.
55  * <em>If your test does not need to load an application context, you may choose
56  * to omit the {@link ContextConfiguration &#064;ContextConfiguration} declaration
57  * and to configure the appropriate
58  * {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners}
59  * manually.</em></li>
60  * <li>Must have constructors which either implicitly or explicitly delegate to
61  * <code>super();</code>.</li>
62  * </ul>
63  *
64  * @author Sam Brannen
65  * @author Juergen Hoeller
66  * @since 2.5
67  * @see TestContext
68  * @see TestContextManager
69  * @see TestExecutionListeners
70  * @see AbstractTransactionalTestNGSpringContextTests
71  * @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
72  * @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
73  */
74 @TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
75 public abstract class AbstractTestNGSpringContextTests implements IHookable, ApplicationContextAware {
76 
77 	/** Logger available to subclasses */
78 	protected final Log logger = LogFactory.getLog(getClass());
79 
80 	/**
81 	 * The {@link ApplicationContext} that was injected into this test instance
82 	 * via {@link #setApplicationContext(ApplicationContext)}.
83 	 */
84 	protected ApplicationContext applicationContext;
85 
86 	private final TestContextManager testContextManager;
87 
88 	private Throwable testException;
89 
90 
91 	/**
92 	 * Construct a new AbstractTestNGSpringContextTests instance and initialize
93 	 * the internal {@link TestContextManager} for the current test.
94 	 */
AbstractTestNGSpringContextTests()95 	public AbstractTestNGSpringContextTests() {
96 		this.testContextManager = new TestContextManager(getClass());
97 	}
98 
99 	/**
100 	 * Set the {@link ApplicationContext} to be used by this test instance,
101 	 * provided via {@link ApplicationContextAware} semantics.
102 	 *
103 	 * @param applicationContext the applicationContext to set
104 	 */
setApplicationContext(ApplicationContext applicationContext)105 	public final void setApplicationContext(ApplicationContext applicationContext) {
106 		this.applicationContext = applicationContext;
107 	}
108 
109 	/**
110 	 * Delegates to the configured {@link TestContextManager} to call
111 	 * {@link TestContextManager#beforeTestClass() 'before test class'}
112 	 * callbacks.
113 	 *
114 	 * @throws Exception if a registered TestExecutionListener throws an
115 	 * exception
116 	 */
117 	@BeforeClass(alwaysRun = true)
springTestContextBeforeTestClass()118 	protected void springTestContextBeforeTestClass() throws Exception {
119 		this.testContextManager.beforeTestClass();
120 	}
121 
122 	/**
123 	 * Delegates to the configured {@link TestContextManager} to
124 	 * {@link TestContextManager#prepareTestInstance(Object) prepare} this test
125 	 * instance prior to execution of any individual tests, for example for
126 	 * injecting dependencies, etc.
127 	 *
128 	 * @throws Exception if a registered TestExecutionListener throws an
129 	 * exception
130 	 */
131 	@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextBeforeTestClass")
springTestContextPrepareTestInstance()132 	protected void springTestContextPrepareTestInstance() throws Exception {
133 		this.testContextManager.prepareTestInstance(this);
134 	}
135 
136 	/**
137 	 * Delegates to the configured {@link TestContextManager} to
138 	 * {@link TestContextManager#beforeTestMethod(Object,Method) pre-process}
139 	 * the test method before the actual test is executed.
140 	 *
141 	 * @param testMethod the test method which is about to be executed.
142 	 * @throws Exception allows all exceptions to propagate.
143 	 */
144 	@BeforeMethod(alwaysRun = true)
springTestContextBeforeTestMethod(Method testMethod)145 	protected void springTestContextBeforeTestMethod(Method testMethod) throws Exception {
146 		this.testContextManager.beforeTestMethod(this, testMethod);
147 	}
148 
149 	/**
150 	 * Delegates to the {@link IHookCallBack#runTestMethod(ITestResult) test
151 	 * method} in the supplied <code>callback</code> to execute the actual test
152 	 * and then tracks the exception thrown during test execution, if any.
153 	 *
154 	 * @see org.testng.IHookable#run(org.testng.IHookCallBack,
155 	 * org.testng.ITestResult)
156 	 */
run(IHookCallBack callBack, ITestResult testResult)157 	public void run(IHookCallBack callBack, ITestResult testResult) {
158 		callBack.runTestMethod(testResult);
159 
160 		Throwable testResultException = testResult.getThrowable();
161 		if (testResultException instanceof InvocationTargetException) {
162 			testResultException = ((InvocationTargetException) testResultException).getCause();
163 		}
164 		this.testException = testResultException;
165 	}
166 
167 	/**
168 	 * Delegates to the configured {@link TestContextManager} to
169 	 * {@link TestContextManager#afterTestMethod(Object, Method, Throwable)
170 	 * post-process} the test method after the actual test has executed.
171 	 *
172 	 * @param testMethod the test method which has just been executed on the
173 	 * test instance
174 	 * @throws Exception allows all exceptions to propagate
175 	 */
176 	@AfterMethod(alwaysRun = true)
springTestContextAfterTestMethod(Method testMethod)177 	protected void springTestContextAfterTestMethod(Method testMethod) throws Exception {
178 		try {
179 			this.testContextManager.afterTestMethod(this, testMethod, this.testException);
180 		}
181 		finally {
182 			this.testException = null;
183 		}
184 	}
185 
186 	/**
187 	 * Delegates to the configured {@link TestContextManager} to call
188 	 * {@link TestContextManager#afterTestClass() 'after test class'} callbacks.
189 	 *
190 	 * @throws Exception if a registered TestExecutionListener throws an
191 	 * exception
192 	 */
193 	@AfterClass(alwaysRun = true)
springTestContextAfterTestClass()194 	protected void springTestContextAfterTestClass() throws Exception {
195 		this.testContextManager.afterTestClass();
196 	}
197 
198 }
199