001/*
002 * Copyright 2002-2014 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.testng;
018
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.context.ApplicationContext;
026import org.springframework.context.ApplicationContextAware;
027import org.springframework.test.context.ContextConfiguration;
028import org.springframework.test.context.TestContext;
029import org.springframework.test.context.TestContextManager;
030import org.springframework.test.context.TestExecutionListeners;
031import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
032import org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener;
033import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
034import org.springframework.test.context.web.ServletTestExecutionListener;
035
036import org.testng.IHookCallBack;
037import org.testng.IHookable;
038import org.testng.ITestResult;
039import org.testng.annotations.AfterClass;
040import org.testng.annotations.AfterMethod;
041import org.testng.annotations.BeforeClass;
042import org.testng.annotations.BeforeMethod;
043
044/**
045 * Abstract base test class which integrates the <em>Spring TestContext Framework</em>
046 * with explicit {@link ApplicationContext} testing support in a <strong>TestNG</strong>
047 * environment.
048 *
049 * <p>Concrete subclasses:
050 * <ul>
051 * <li>Typically declare a class-level {@link ContextConfiguration
052 * &#064;ContextConfiguration} annotation to configure the {@link ApplicationContext
053 * application context} {@link ContextConfiguration#locations() resource locations}
054 * or {@link ContextConfiguration#classes() annotated classes}. <em>If your test
055 * does not need to load an application context, you may choose to omit the
056 * {@link ContextConfiguration &#064;ContextConfiguration} declaration and to
057 * configure the appropriate
058 * {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners}
059 * manually.</em></li>
060 * <li>Must have constructors which either implicitly or explicitly delegate to
061 * {@code super();}.</li>
062 * </ul>
063 *
064 * <p>The following {@link org.springframework.test.context.TestExecutionListener
065 * TestExecutionListeners} are configured by default:
066 *
067 * <ul>
068 * <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
069 * <li>{@link org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener}
070 * <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
071 * <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
072 * </ul>
073 *
074 * @author Sam Brannen
075 * @author Juergen Hoeller
076 * @since 2.5
077 * @see ContextConfiguration
078 * @see TestContext
079 * @see TestContextManager
080 * @see TestExecutionListeners
081 * @see ServletTestExecutionListener
082 * @see DirtiesContextBeforeModesTestExecutionListener
083 * @see DependencyInjectionTestExecutionListener
084 * @see DirtiesContextTestExecutionListener
085 * @see AbstractTransactionalTestNGSpringContextTests
086 * @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
087 */
088@TestExecutionListeners({ ServletTestExecutionListener.class, DirtiesContextBeforeModesTestExecutionListener.class,
089        DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
090public abstract class AbstractTestNGSpringContextTests implements IHookable, ApplicationContextAware {
091
092        /** Logger available to subclasses */
093        protected final Log logger = LogFactory.getLog(getClass());
094
095        /**
096         * The {@link ApplicationContext} that was injected into this test instance
097         * via {@link #setApplicationContext(ApplicationContext)}.
098         */
099        protected ApplicationContext applicationContext;
100
101        private final TestContextManager testContextManager;
102
103        private Throwable testException;
104
105
106        /**
107         * Construct a new AbstractTestNGSpringContextTests instance and initialize
108         * the internal {@link TestContextManager} for the current test.
109         */
110        public AbstractTestNGSpringContextTests() {
111                this.testContextManager = new TestContextManager(getClass());
112        }
113
114        /**
115         * Set the {@link ApplicationContext} to be used by this test instance,
116         * provided via {@link ApplicationContextAware} semantics.
117         *
118         * @param applicationContext the ApplicationContext that this test runs in
119         */
120        @Override
121        public final void setApplicationContext(ApplicationContext applicationContext) {
122                this.applicationContext = applicationContext;
123        }
124
125        /**
126         * Delegates to the configured {@link TestContextManager} to call
127         * {@link TestContextManager#beforeTestClass() 'before test class'}
128         * callbacks.
129         *
130         * @throws Exception if a registered TestExecutionListener throws an
131         * exception
132         */
133        @BeforeClass(alwaysRun = true)
134        protected void springTestContextBeforeTestClass() throws Exception {
135                this.testContextManager.beforeTestClass();
136        }
137
138        /**
139         * Delegates to the configured {@link TestContextManager} to
140         * {@link TestContextManager#prepareTestInstance(Object) prepare} this test
141         * instance prior to execution of any individual tests, for example for
142         * injecting dependencies, etc.
143         *
144         * @throws Exception if a registered TestExecutionListener throws an
145         * exception
146         */
147        @BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextBeforeTestClass")
148        protected void springTestContextPrepareTestInstance() throws Exception {
149                this.testContextManager.prepareTestInstance(this);
150        }
151
152        /**
153         * Delegates to the configured {@link TestContextManager} to
154         * {@link TestContextManager#beforeTestMethod(Object,Method) pre-process}
155         * the test method before the actual test is executed.
156         *
157         * @param testMethod the test method which is about to be executed.
158         * @throws Exception allows all exceptions to propagate.
159         */
160        @BeforeMethod(alwaysRun = true)
161        protected void springTestContextBeforeTestMethod(Method testMethod) throws Exception {
162                this.testContextManager.beforeTestMethod(this, testMethod);
163        }
164
165        /**
166         * Delegates to the {@link IHookCallBack#runTestMethod(ITestResult) test
167         * method} in the supplied {@code callback} to execute the actual test
168         * and then tracks the exception thrown during test execution, if any.
169         *
170         * @see org.testng.IHookable#run(org.testng.IHookCallBack,
171         * org.testng.ITestResult)
172         */
173        @Override
174        public void run(IHookCallBack callBack, ITestResult testResult) {
175                callBack.runTestMethod(testResult);
176
177                Throwable testResultException = testResult.getThrowable();
178                if (testResultException instanceof InvocationTargetException) {
179                        testResultException = ((InvocationTargetException) testResultException).getCause();
180                }
181                this.testException = testResultException;
182        }
183
184        /**
185         * Delegates to the configured {@link TestContextManager} to
186         * {@link TestContextManager#afterTestMethod(Object, Method, Throwable)
187         * post-process} the test method after the actual test has executed.
188         *
189         * @param testMethod the test method which has just been executed on the
190         * test instance
191         * @throws Exception allows all exceptions to propagate
192         */
193        @AfterMethod(alwaysRun = true)
194        protected void springTestContextAfterTestMethod(Method testMethod) throws Exception {
195                try {
196                        this.testContextManager.afterTestMethod(this, testMethod, this.testException);
197                }
198                finally {
199                        this.testException = null;
200                }
201        }
202
203        /**
204         * Delegates to the configured {@link TestContextManager} to call
205         * {@link TestContextManager#afterTestClass() 'after test class'} callbacks.
206         *
207         * @throws Exception if a registered TestExecutionListener throws an
208         * exception
209         */
210        @AfterClass(alwaysRun = true)
211        protected void springTestContextAfterTestClass() throws Exception {
212                this.testContextManager.afterTestClass();
213        }
214
215}