Skip to content

Commit 0f6711f

Browse files
committed
Support @[Before|After]Transaction on default methods
Prior to this commit, @BeforeTransaction and @AfterTransaction could only be declared on methods within test classes. However, JUnit 5 as well as some existing third-party Runner implementations for JUnit 4 already support Java 8 based interface default methods in various scenarios -- for example, @test, @beforeeach, etc. This commit brings the Spring TestContext Framework up to date by supporting the declaration of @BeforeTransaction and @AfterTransaction on interface default methods. Issue: SPR-14183
1 parent 7ce5ba4 commit 0f6711f

File tree

6 files changed

+46
-101
lines changed

6 files changed

+46
-101
lines changed

spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
import java.lang.annotation.Target;
2424

2525
/**
26-
* <p>Test annotation to indicate that the annotated {@code void} method
26+
* <p>Test annotation which indicates that the annotated {@code void} method
2727
* should be executed <em>after</em> a transaction is ended for a test method
28-
* configured to run within a transaction via the {@code @Transactional} annotation.
28+
* configured to run within a transaction via Spring's {@code @Transactional}
29+
* annotation.
2930
*
30-
* <p>The {@code @AfterTransaction} methods of superclasses will be executed
31-
* after those of the current class.
31+
* <p>As of Spring Framework 4.3, {@code @AfterTransaction} may be declared on
32+
* Java 8 based interface default methods.
33+
*
34+
* <p>{@code @AfterTransaction} methods declared in superclasses or as interface
35+
* default methods will be executed after those of the current test class.
3236
*
3337
* <p>As of Spring Framework 4.0, this annotation may be used as a
3438
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.

spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
import java.lang.annotation.Target;
2424

2525
/**
26-
* <p>Test annotation to indicate that the annotated {@code void} method
26+
* <p>Test annotation which indicates that the annotated {@code void} method
2727
* should be executed <em>before</em> a transaction is started for a test method
28-
* configured to run within a transaction via the {@code @Transactional} annotation.
28+
* configured to run within a transaction via Spring's {@code @Transactional}
29+
* annotation.
2930
*
30-
* <p>The {@code @BeforeTransaction} methods of superclasses will be executed
31-
* before those of the current class.
31+
* <p>As of Spring Framework 4.3, {@code @BeforeTransaction} may be declared on
32+
* Java 8 based interface default methods.
33+
*
34+
* <p>{@code @BeforeTransaction} methods declared in superclasses or as interface
35+
* default methods will be executed before those of the current test class.
3236
*
3337
* <p>As of Spring Framework 4.0, this annotation may be used as a
3438
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.

spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java

Lines changed: 12 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,10 @@
9797
* <p>When executing transactional tests, it is sometimes useful to be able to
9898
* execute certain <em>set up</em> or <em>tear down</em> code outside of a
9999
* transaction. {@code TransactionalTestExecutionListener} provides such
100-
* support for methods annotated with
101-
* {@link BeforeTransaction @BeforeTransaction} or
102-
* {@link AfterTransaction @AfterTransaction}.
100+
* support for methods annotated with {@link BeforeTransaction @BeforeTransaction}
101+
* or {@link AfterTransaction @AfterTransaction}. As of Spring Framework 4.3,
102+
* {@code @BeforeTransaction} and {@code @AfterTransaction} may also be declared
103+
* on Java 8 based interface default methods.
103104
*
104105
* <h3>Configuring a Transaction Manager</h3>
105106
* <p>{@code TransactionalTestExecutionListener} expects a
@@ -431,90 +432,23 @@ protected final boolean isRollback(TestContext testContext) throws Exception {
431432
}
432433

433434
/**
434-
* Gets all superclasses of the supplied {@link Class class}, including the
435-
* class itself. The ordering of the returned list will begin with the
436-
* supplied class and continue up the class hierarchy, excluding {@link Object}.
437-
* <p>Note: This code has been borrowed from
438-
* {@link org.junit.internal.runners.TestClass#getSuperClasses(Class)} and
439-
* adapted.
440-
* @param clazz the class for which to retrieve the superclasses
441-
* @return all superclasses of the supplied class, excluding {@code Object}
442-
*/
443-
private List<Class<?>> getSuperClasses(Class<?> clazz) {
444-
List<Class<?>> results = new ArrayList<Class<?>>();
445-
Class<?> current = clazz;
446-
while (current != null && Object.class != current) {
447-
results.add(current);
448-
current = current.getSuperclass();
449-
}
450-
return results;
451-
}
452-
453-
/**
454-
* Gets all methods in the supplied {@link Class class} and its superclasses
435+
* Get all methods in the supplied {@link Class class} and its superclasses
455436
* which are annotated with the supplied {@code annotationType} but
456437
* which are not <em>shadowed</em> by methods overridden in subclasses.
457-
* <p>Note: This code has been borrowed from
458-
* {@link org.junit.internal.runners.TestClass#getAnnotatedMethods(Class)}
459-
* and adapted.
438+
* <p>Default methods on interfaces are also detected.
460439
* @param clazz the class for which to retrieve the annotated methods
461440
* @param annotationType the annotation type for which to search
462441
* @return all annotated methods in the supplied class and its superclasses
442+
* as well as annotated interface default methods
463443
*/
464444
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
465-
List<Method> results = new ArrayList<Method>();
466-
for (Class<?> current : getSuperClasses(clazz)) {
467-
for (Method method : current.getDeclaredMethods()) {
468-
Annotation annotation = AnnotationUtils.getAnnotation(method, annotationType);
469-
if (annotation != null && !isShadowed(method, results)) {
470-
results.add(method);
471-
}
472-
}
473-
}
474-
return results;
475-
}
476-
477-
/**
478-
* Determine if the supplied {@link Method method} is <em>shadowed</em> by
479-
* a method in the supplied {@link List list} of previous methods.
480-
* <p>Note: This code has been borrowed from
481-
* {@link org.junit.internal.runners.TestClass#isShadowed(Method, List)}.
482-
* @param method the method to check for shadowing
483-
* @param previousMethods the list of methods which have previously been processed
484-
* @return {@code true} if the supplied method is shadowed by a
485-
* method in the {@code previousMethods} list
486-
*/
487-
private boolean isShadowed(Method method, List<Method> previousMethods) {
488-
for (Method each : previousMethods) {
489-
if (isShadowed(method, each)) {
490-
return true;
491-
}
492-
}
493-
return false;
494-
}
495-
496-
/**
497-
* Determine if the supplied {@linkplain Method current method} is
498-
* <em>shadowed</em> by a {@linkplain Method previous method}.
499-
* <p>Note: This code has been borrowed from
500-
* {@link org.junit.internal.runners.TestClass#isShadowed(Method, Method)}.
501-
* @param current the current method
502-
* @param previous the previous method
503-
* @return {@code true} if the previous method shadows the current one
504-
*/
505-
private boolean isShadowed(Method current, Method previous) {
506-
if (!previous.getName().equals(current.getName())) {
507-
return false;
508-
}
509-
if (previous.getParameterTypes().length != current.getParameterTypes().length) {
510-
return false;
511-
}
512-
for (int i = 0; i < previous.getParameterTypes().length; i++) {
513-
if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) {
514-
return false;
445+
List<Method> methods = new ArrayList<Method>(4);
446+
for (Method method : ReflectionUtils.getUniqueDeclaredMethods(clazz)) {
447+
if (AnnotationUtils.getAnnotation(method, annotationType) != null) {
448+
methods.add(method);
515449
}
516450
}
517-
return true;
451+
return methods;
518452
}
519453

520454
/**

spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.lang.annotation.RetentionPolicy;
2121

2222
import org.junit.After;
23-
import org.junit.Ignore;
2423
import org.junit.Rule;
2524
import org.junit.Test;
2625
import org.junit.rules.ExpectedException;
@@ -245,13 +244,11 @@ public void afterTestMethodWithAfterTransactionDeclaredViaMetaAnnotation() throw
245244
assertAfterTestMethod(AfterTransactionDeclaredViaMetaAnnotationTestCase.class);
246245
}
247246

248-
@Ignore("Disabled until @BeforeTransaction is supported on interface default methods")
249247
@Test
250248
public void beforeTestMethodWithBeforeTransactionDeclaredAsInterfaceDefaultMethod() throws Exception {
251249
assertBeforeTestMethod(BeforeTransactionDeclaredAsInterfaceDefaultMethodTestCase.class);
252250
}
253251

254-
@Ignore("Disabled until @AfterTransaction is supported on interface default methods")
255252
@Test
256253
public void afterTestMethodWithAfterTransactionDeclaredAsInterfaceDefaultMethod() throws Exception {
257254
assertAfterTestMethod(AfterTransactionDeclaredAsInterfaceDefaultMethodTestCase.class);

src/asciidoc/testing.adoc

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -854,9 +854,12 @@ method, potentially overriding class-level `@Rollback` or `@Commit` semantics.
854854

855855
+
856856

857-
Indicates that the annotated `void` method should be executed __before__ a
858-
transaction is started for test methods configured to run within a transaction via the
859-
`@Transactional` annotation.
857+
Indicates that the annotated `void` method should be executed __before__ a transaction
858+
is started for test methods configured to run within a transaction via Spring's
859+
`@Transactional` annotation. As of Spring Framework 4.3, `@BeforeTransaction` methods
860+
are not required to be `public` and may be declared on Java 8 based interface default
861+
methods.
862+
860863

861864
+
862865

@@ -873,9 +876,11 @@ transaction is started for test methods configured to run within a transaction v
873876

874877
+
875878

876-
Indicates that the annotated `void` method should be executed __after__ a
877-
transaction has ended for test methods configured to run within a transaction via the
878-
`@Transactional` annotation.
879+
Indicates that the annotated `void` method should be executed __after__ a transaction
880+
is ended for test methods configured to run within a transaction via Spring's
881+
`@Transactional` annotation. As of Spring Framework 4.3, `@AfterTransaction` methods
882+
are not required to be `public` and may be declared on Java 8 based interface default
883+
methods.
879884

880885
+
881886

@@ -3210,12 +3215,12 @@ javadocs for `TestTransaction` for further details.
32103215
Occasionally you need to execute certain code before or after a transactional test method
32113216
but outside the transactional context -- for example, to verify the initial database state
32123217
prior to execution of your test or to verify expected transactional commit behavior after
3213-
test execution (if the test was configured not to roll back the transaction).
3218+
test execution (if the test was configured to commit the transaction).
32143219
`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and
32153220
`@AfterTransaction` annotations exactly for such scenarios. Simply annotate any `void`
3216-
method in your test class with one of these annotations, and the
3217-
`TransactionalTestExecutionListener` ensures that your __before transaction method__ or
3218-
__after transaction method__ is executed at the appropriate time.
3221+
method in a test class or any `void` default method in a test interface with one of these
3222+
annotations, and the `TransactionalTestExecutionListener` ensures that your __before
3223+
transaction method__ or __after transaction method__ is executed at the appropriate time.
32193224

32203225
[TIP]
32213226
====

src/asciidoc/whats-new.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,9 @@ Spring 4.3 also improves the caching abstraction as follows:
694694
* New `SpringRunner` _alias_ for the `SpringJUnit4ClassRunner`.
695695
* An empty declaration of `@ContextConfiguration` can now be completely omitted if default
696696
XML files, Groovy scripts, or `@Configuration` classes are detected.
697-
* `@Transactional` test methods are no longer required to be `public` (in TestNG and JUnit 5).
698-
* `@BeforeTransaction` and `@AfterTransaction` methods are no longer required to be `public`.
697+
* `@Transactional` test methods are no longer required to be `public` (e.g., in TestNG and JUnit 5).
698+
* `@BeforeTransaction` and `@AfterTransaction` methods are no longer required to be `public`
699+
and may now be declared on Java 8 based interface default methods.
699700
* The `ApplicationContext` cache in the _Spring TestContext Framework_ is now bounded with a
700701
default maximum size of 32 and a _least recently used_ eviction policy. The maximum size
701702
can be configured by setting a JVM system property or Spring property called

0 commit comments

Comments
 (0)