PageRenderTime 53ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/org.springframework.orm/src/test/java/org/springframework/test/annotation/AbstractAnnotationAwareTransactionalTests.java

https://github.com/loicfrering/spring-framework
Java | 311 lines | 166 code | 31 blank | 114 comment | 34 complexity | 12e2647cc5a159b6b70e7af09ca30cb3 MD5 | raw file
  1. /*
  2. * Copyright 2002-2008 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. package org.springframework.test.annotation;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.Modifier;
  19. import java.util.Map;
  20. import javax.sql.DataSource;
  21. import junit.framework.AssertionFailedError;
  22. import org.springframework.context.ApplicationContext;
  23. import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
  24. import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
  25. import org.springframework.transaction.TransactionDefinition;
  26. import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
  27. import org.springframework.transaction.interceptor.TransactionAttributeSource;
  28. import org.springframework.util.Assert;
  29. /**
  30. * <p>
  31. * Java 5 specific subclass of
  32. * {@link AbstractTransactionalDataSourceSpringContextTests}, exposing a
  33. * {@link SimpleJdbcTemplate} and obeying annotations for transaction control.
  34. * </p>
  35. * <p>
  36. * For example, test methods can be annotated with the regular Spring
  37. * {@link org.springframework.transaction.annotation.Transactional @Transactional}
  38. * annotation (e.g., to force execution in a read-only transaction) or with the
  39. * {@link NotTransactional @NotTransactional} annotation to prevent any
  40. * transaction being created at all. In addition, individual test methods can be
  41. * annotated with {@link Rollback @Rollback} to override the
  42. * {@link #isDefaultRollback() default rollback} settings.
  43. * </p>
  44. * <p>
  45. * The following list constitutes all annotations currently supported by
  46. * AbstractAnnotationAwareTransactionalTests:
  47. * </p>
  48. * <ul>
  49. * <li>{@link DirtiesContext @DirtiesContext}</li>
  50. * <li>{@link ProfileValueSourceConfiguration @ProfileValueSourceConfiguration}</li>
  51. * <li>{@link IfProfileValue @IfProfileValue}</li>
  52. * <li>{@link ExpectedException @ExpectedException}</li>
  53. * <li>{@link Timed @Timed}</li>
  54. * <li>{@link Repeat @Repeat}</li>
  55. * <li>{@link org.springframework.transaction.annotation.Transactional @Transactional}</li>
  56. * <li>{@link NotTransactional @NotTransactional}</li>
  57. * <li>{@link Rollback @Rollback}</li>
  58. * </ul>
  59. *
  60. * @author Rod Johnson
  61. * @author Sam Brannen
  62. * @author Juergen Hoeller
  63. * @since 2.0
  64. * @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
  65. * ({@link org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests})
  66. */
  67. public abstract class AbstractAnnotationAwareTransactionalTests extends
  68. AbstractTransactionalDataSourceSpringContextTests {
  69. protected SimpleJdbcTemplate simpleJdbcTemplate;
  70. private final TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
  71. /**
  72. * {@link ProfileValueSource} available to subclasses but primarily intended
  73. * for use in {@link #isDisabledInThisEnvironment(Method)}.
  74. * <p>Set to {@link SystemProfileValueSource} by default for backwards
  75. * compatibility; however, the value may be changed in the
  76. * {@link #AbstractAnnotationAwareTransactionalTests(String)} constructor.
  77. */
  78. protected ProfileValueSource profileValueSource = SystemProfileValueSource.getInstance();
  79. /**
  80. * Default constructor for AbstractAnnotationAwareTransactionalTests, which
  81. * delegates to {@link #AbstractAnnotationAwareTransactionalTests(String)}.
  82. */
  83. public AbstractAnnotationAwareTransactionalTests() {
  84. this(null);
  85. }
  86. /**
  87. * Constructs a new AbstractAnnotationAwareTransactionalTests instance with
  88. * the specified JUnit <code>name</code> and retrieves the configured (or
  89. * default) {@link ProfileValueSource}.
  90. * @param name the name of the current test
  91. * @see ProfileValueUtils#retrieveProfileValueSource(Class)
  92. */
  93. public AbstractAnnotationAwareTransactionalTests(String name) {
  94. super(name);
  95. this.profileValueSource = ProfileValueUtils.retrieveProfileValueSource(getClass());
  96. }
  97. @Override
  98. public void setDataSource(DataSource dataSource) {
  99. super.setDataSource(dataSource);
  100. // JdbcTemplate will be identically configured
  101. this.simpleJdbcTemplate = new SimpleJdbcTemplate(this.jdbcTemplate);
  102. }
  103. /**
  104. * Search for a unique {@link ProfileValueSource} in the supplied
  105. * {@link ApplicationContext}. If found, the
  106. * <code>profileValueSource</code> for this test will be set to the unique
  107. * {@link ProfileValueSource}.
  108. * @param applicationContext the ApplicationContext in which to search for
  109. * the ProfileValueSource; may not be <code>null</code>
  110. * @deprecated Use {@link ProfileValueSourceConfiguration @ProfileValueSourceConfiguration} instead.
  111. */
  112. @Deprecated
  113. protected void findUniqueProfileValueSourceFromContext(ApplicationContext applicationContext) {
  114. Assert.notNull(applicationContext, "Can not search for a ProfileValueSource in a null ApplicationContext.");
  115. ProfileValueSource uniqueProfileValueSource = null;
  116. Map<?, ?> beans = applicationContext.getBeansOfType(ProfileValueSource.class);
  117. if (beans.size() == 1) {
  118. uniqueProfileValueSource = (ProfileValueSource) beans.values().iterator().next();
  119. }
  120. if (uniqueProfileValueSource != null) {
  121. this.profileValueSource = uniqueProfileValueSource;
  122. }
  123. }
  124. /**
  125. * Overridden to populate transaction definition from annotations.
  126. */
  127. @Override
  128. public void runBare() throws Throwable {
  129. // getName will return the name of the method being run.
  130. if (isDisabledInThisEnvironment(getName())) {
  131. // Let superclass log that we didn't run the test.
  132. super.runBare();
  133. return;
  134. }
  135. final Method testMethod = getTestMethod();
  136. if (isDisabledInThisEnvironment(testMethod)) {
  137. recordDisabled();
  138. this.logger.info("**** " + getClass().getName() + "." + getName() + " is disabled in this environment: "
  139. + "Total disabled tests=" + getDisabledTestCount());
  140. return;
  141. }
  142. TransactionDefinition explicitTransactionDefinition =
  143. this.transactionAttributeSource.getTransactionAttribute(testMethod, getClass());
  144. if (explicitTransactionDefinition != null) {
  145. this.logger.info("Custom transaction definition [" + explicitTransactionDefinition + "] for test method ["
  146. + getName() + "].");
  147. setTransactionDefinition(explicitTransactionDefinition);
  148. }
  149. else if (testMethod.isAnnotationPresent(NotTransactional.class)) {
  150. // Don't have any transaction...
  151. preventTransaction();
  152. }
  153. // Let JUnit handle execution. We're just changing the state of the test class first.
  154. runTestTimed(new TestExecutionCallback() {
  155. public void run() throws Throwable {
  156. try {
  157. AbstractAnnotationAwareTransactionalTests.super.runBare();
  158. }
  159. finally {
  160. // Mark the context to be blown away if the test was
  161. // annotated to result in setDirty being invoked
  162. // automatically.
  163. if (testMethod.isAnnotationPresent(DirtiesContext.class)) {
  164. AbstractAnnotationAwareTransactionalTests.this.setDirty();
  165. }
  166. }
  167. }
  168. }, testMethod);
  169. }
  170. /**
  171. * Determine if the test for the supplied <code>testMethod</code> should
  172. * run in the current environment.
  173. * <p>The default implementation is based on
  174. * {@link IfProfileValue @IfProfileValue} semantics.
  175. * @param testMethod the test method
  176. * @return <code>true</code> if the test is <em>disabled</em> in the current environment
  177. * @see ProfileValueUtils#isTestEnabledInThisEnvironment
  178. */
  179. protected boolean isDisabledInThisEnvironment(Method testMethod) {
  180. return !ProfileValueUtils.isTestEnabledInThisEnvironment(this.profileValueSource, testMethod, getClass());
  181. }
  182. /**
  183. * Get the current test method.
  184. */
  185. protected Method getTestMethod() {
  186. assertNotNull("TestCase.getName() cannot be null", getName());
  187. Method testMethod = null;
  188. try {
  189. // Use same algorithm as JUnit itself to retrieve the test method
  190. // about to be executed (the method name is returned by getName). It
  191. // has to be public so we can retrieve it.
  192. testMethod = getClass().getMethod(getName(), (Class[]) null);
  193. }
  194. catch (NoSuchMethodException ex) {
  195. fail("Method '" + getName() + "' not found");
  196. }
  197. if (!Modifier.isPublic(testMethod.getModifiers())) {
  198. fail("Method '" + getName() + "' should be public");
  199. }
  200. return testMethod;
  201. }
  202. /**
  203. * Determine whether or not to rollback transactions for the current test
  204. * by taking into consideration the
  205. * {@link #isDefaultRollback() default rollback} flag and a possible
  206. * method-level override via the {@link Rollback @Rollback} annotation.
  207. * @return the <em>rollback</em> flag for the current test
  208. */
  209. @Override
  210. protected boolean isRollback() {
  211. boolean rollback = isDefaultRollback();
  212. Rollback rollbackAnnotation = getTestMethod().getAnnotation(Rollback.class);
  213. if (rollbackAnnotation != null) {
  214. boolean rollbackOverride = rollbackAnnotation.value();
  215. if (this.logger.isDebugEnabled()) {
  216. this.logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback ["
  217. + rollback + "] for test [" + getName() + "].");
  218. }
  219. rollback = rollbackOverride;
  220. }
  221. else {
  222. if (this.logger.isDebugEnabled()) {
  223. this.logger.debug("No method-level @Rollback override: using default rollback [" + rollback
  224. + "] for test [" + getName() + "].");
  225. }
  226. }
  227. return rollback;
  228. }
  229. private void runTestTimed(TestExecutionCallback tec, Method testMethod) throws Throwable {
  230. Timed timed = testMethod.getAnnotation(Timed.class);
  231. if (timed == null) {
  232. runTest(tec, testMethod);
  233. }
  234. else {
  235. long startTime = System.currentTimeMillis();
  236. try {
  237. runTest(tec, testMethod);
  238. }
  239. finally {
  240. long elapsed = System.currentTimeMillis() - startTime;
  241. if (elapsed > timed.millis()) {
  242. fail("Took " + elapsed + " ms; limit was " + timed.millis());
  243. }
  244. }
  245. }
  246. }
  247. private void runTest(TestExecutionCallback tec, Method testMethod) throws Throwable {
  248. ExpectedException expectedExceptionAnnotation = testMethod.getAnnotation(ExpectedException.class);
  249. boolean exceptionIsExpected = (expectedExceptionAnnotation != null && expectedExceptionAnnotation.value() != null);
  250. Class<? extends Throwable> expectedException = (exceptionIsExpected ? expectedExceptionAnnotation.value() : null);
  251. Repeat repeat = testMethod.getAnnotation(Repeat.class);
  252. int runs = ((repeat != null) && (repeat.value() > 1)) ? repeat.value() : 1;
  253. for (int i = 0; i < runs; i++) {
  254. try {
  255. if (runs > 1 && this.logger != null && this.logger.isInfoEnabled()) {
  256. this.logger.info("Repetition " + (i + 1) + " of test " + testMethod.getName());
  257. }
  258. tec.run();
  259. if (exceptionIsExpected) {
  260. fail("Expected exception: " + expectedException.getName());
  261. }
  262. }
  263. catch (Throwable t) {
  264. if (!exceptionIsExpected) {
  265. throw t;
  266. }
  267. if (!expectedException.isAssignableFrom(t.getClass())) {
  268. // Wrap the unexpected throwable with an explicit message.
  269. AssertionFailedError assertionError = new AssertionFailedError("Unexpected exception, expected<" +
  270. expectedException.getName() + "> but was<" + t.getClass().getName() + ">");
  271. assertionError.initCause(t);
  272. throw assertionError;
  273. }
  274. }
  275. }
  276. }
  277. private static interface TestExecutionCallback {
  278. void run() throws Throwable;
  279. }
  280. }