Document JUnit Jupiter annotation support in the reference manual

Issue: SPR-14524
This commit is contained in:
Sam Brannen 2017-09-27 16:25:16 +02:00
parent 1a8122f97a
commit 550bed2905
1 changed files with 269 additions and 40 deletions

View File

@ -866,19 +866,20 @@ however, these lifecycle annotations have limited usage within an actual test cl
If a method within a test class is annotated with `@PostConstruct`, that method will be
executed before any __before__ methods of the underlying test framework (e.g., methods
annotated with JUnit 4's `@Before`), and that will apply for every test method in the test
class. On the other hand, if a method within a test class is annotated with
annotated with JUnit Jupiter's `@BeforeEach`), and that will apply for every test method
in the test class. On the other hand, if a method within a test class is annotated with
`@PreDestroy`, that method will __never__ be executed. Within a test class it is
therefore recommended to use test lifecycle callbacks from the underlying test framework
instead of `@PostConstruct` and `@PreDestroy`.
====
[[integration-testing-annotations-junit]]
[[integration-testing-annotations-junit4]]
==== Spring JUnit 4 Testing Annotations
The following annotations are __only__ supported when used in conjunction with the
<<testcontext-junit4-runner,SpringRunner>>, <<testcontext-junit4-rules,Spring's JUnit
rules>>, or <<testcontext-support-classes-junit4,Spring's JUnit 4 support classes>>.
4 rules>>, or <<testcontext-support-classes-junit4,Spring's JUnit 4 support classes>>.
===== @IfProfileValue
`@IfProfileValue` indicates that the annotated test is enabled for a specific testing
@ -974,6 +975,147 @@ well as any __set up__ or __tear down__ of the test fixture.
}
----
[[integration-testing-annotations-junit-jupiter]]
==== Spring JUnit Jupiter Testing Annotations
The following annotations are __only__ supported when used in conjunction with the
`SpringExtension` and JUnit Jupiter (i.e., the programming model in JUnit 5).
===== @SpringJUnitConfig
`@SpringJUnitConfig` is a _composed annotation_ that combines
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` from
the Spring TestContext Framework. It can be used at the class level as a drop-in
replacement for `@ContextConfiguration`. With regard to configuration options, the only
difference between `@ContextConfiguration` and `@SpringJUnitConfig` is that annotated
classes may be declared via the `value` attribute in `@SpringJUnitConfig`.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@SpringJUnitConfig**(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringTests {
// class body...
}
----
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@SpringJUnitConfig**(**locations** = "/test-config.xml")
class XmlJUnitJupiterSpringTests {
// class body...
}
----
See <<testcontext-ctx-management>> as well as the javadocs for `@SpringJUnitConfig` and
`@ContextConfiguration` for further details.
===== @SpringJUnitWebConfig
`@SpringJUnitWebConfig` is a _composed annotation_ that combines
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` and
`@WebAppConfiguration` from the Spring TestContext Framework. It can be used at the class
level as a drop-in replacement for `@ContextConfiguration` and `@WebAppConfiguration`.
With regard to configuration options, the only difference between `@ContextConfiguration`
and `@SpringJUnitWebConfig` is that annotated classes may be declared via the `value`
attribute in `@SpringJUnitWebConfig`. In addition, the `value` attribute from
`@WebAppConfiguration` can only be overridden via the `resourcePath` attribute in
`@SpringJUnitWebConfig`.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@SpringJUnitWebConfig**(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringWebTests {
// class body...
}
----
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@SpringJUnitWebConfig**(**locations** = "/test-config.xml")
class XmlJUnitJupiterSpringWebTests {
// class body...
}
----
See <<testcontext-ctx-management>> as well as the javadocs for `@SpringJUnitWebConfig`,
`@ContextConfiguration`, and `@WebAppConfiguration` for further details.
===== @EnabledIf
`@EnabledIf` is used to signal that the annotated JUnit Jupiter test class or test method
is _enabled_ and should be executed if the supplied `expression` evaluates to `true`.
Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal to
`"true"` (ignoring case), the test will be __enabled__. When applied at the class level,
all test methods within that class are automatically enabled by default as well.
Expressions can be any of the following.
* Spring Expression Language (SpEL) expression for example:
- `@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
* Placeholder for a property available in the Spring `Environment` for example:
- `@EnabledIf("${smoke.tests.enabled}")`
* Text literal for example:
- `@EnabledIf("true")`
Note, however, that a text literal which is _not_ the result of dynamic resolution of a
property placeholder is of zero practical value since `@EnabledIf("false")` is equivalent
to `@Disabled` and `@EnabledIf("true")` is logically meaningless.
`@EnabledIf` may be used as a meta-annotation to create custom composed annotations. For
example, a custom `@EnabledOnMac` annotation can be created as follows.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@EnabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Enabled on Mac OS"
)
public @interface EnabledOnMac {}
----
===== @DisabledIf
`@DisabledIf` is used to signal that the annotated JUnit Jupiter test class or test
method is _disabled_ and should not be executed if the supplied `expression` evaluates to
`true`. Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal
to `"true"` (ignoring case), the test will be __disabled__. When applied at the class
level, all test methods within that class are automatically disabled as well.
Expressions can be any of the following.
* Spring Expression Language (SpEL) expression for example:
- `@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
* Placeholder for a property available in the Spring `Environment` for example:
- `@DisabledIf("${smoke.tests.disabled}")`
* Text literal for example:
- `@DisabledIf("true")`
Note, however, that a text literal which is _not_ the result of dynamic resolution of a
property placeholder is of zero practical value since `@DisabledIf("true")` is
equivalent to `@Disabled` and `@DisabledIf("false")` is logically meaningless.
`@DisabledIf` may be used as a meta-annotation to create custom composed annotations. For
example, a custom `@DisabledOnMac` annotation can be created as follows.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Disabled on Mac OS"
)
public @interface DisabledOnMac {}
----
[[integration-testing-annotations-meta]]
==== Meta-Annotation Support for Testing
@ -1000,13 +1142,17 @@ Each of the following may be used as meta-annotations in conjunction with the
* `@Sql`
* `@SqlConfig`
* `@SqlGroup`
* `@Repeat`
* `@Timed`
* `@IfProfileValue`
* `@ProfileValueSourceConfiguration`
* `@Repeat` _(JUnit 4)_
* `@Timed` _(JUnit 4)_
* `@IfProfileValue` _(JUnit 4)_
* `@ProfileValueSourceConfiguration` _(JUnit 4)_
* `@SpringJUnitConfig` _(JUnit Jupiter)_
* `@SpringJUnitWebConfig` _(JUnit Jupiter)_
* `@EnabledIf` _(JUnit Jupiter)_
* `@DisabledIf` _(JUnit Jupiter)_
For example, if we discover that we are repeating the following configuration
across our JUnit 4 based test suite...
For example, if we discover that we are repeating the following configuration across our
_JUnit 4_ based test suite...
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1024,8 +1170,8 @@ across our JUnit 4 based test suite...
public class UserRepositoryTests { }
----
We can reduce the above duplication by introducing a custom _composed annotation_
that centralizes the common test configuration like this:
We can reduce the above duplication by introducing a custom _composed annotation_ that
centralizes the common test configuration for Spring like this:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1035,25 +1181,106 @@ that centralizes the common test configuration like this:
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTest { }
public @interface TransactionalDevTestConfig { }
----
Then we can use our custom `@TransactionalDevTest` annotation to simplify the
configuration of individual test classes as follows:
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
configuration of individual JUnit 4 based test classes as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@RunWith(SpringRunner.class)
@TransactionalDevTest
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTest
@TransactionalDevTestConfig
public class UserRepositoryTests { }
----
For further details, consult the <<core.adoc#annotation-programming-model,Spring Annotation Programming Model>>.
If we are writing tests using JUnit Jupiter, we can reduce code duplication even further
since annotations in JUnit 5 can also be used as meta-annotations. For example, if we
discover that we are repeating the following configuration across our JUnit Jupiter based
test suite...
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
----
We can reduce the above duplication by introducing a custom _composed annotation_
that centralizes the common test configuration for Spring and JUnit Jupiter like this:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
----
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
configuration of individual JUnit Jupiter based test classes as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
----
Since JUnit Jupiter supports the use of `@Test`, `@RepeatedTest`, `ParameterizedTest`,
etc. as meta-annotations, it is also possible to create custom composed annotations at
the test method level. For example, if we wish to create a _composed annotation_ that
combines the `@Test` and `@Tag` annotations from JUnit Jupiter with the `@Transactional`
annotation from Spring, we could create an `@TransactionalIntegrationTest` annotation as
follows.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }
----
Then we can use our custom `@TransactionalIntegrationTest` annotation to simplify the
configuration of individual JUnit Jupiter based test methods as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@TransactionalIntegrationTest
void saveOrder() { }
@TransactionalIntegrationTest
void deleteOrder() { }
----
For further details, consult the <<core.adoc#annotation-programming-model,Spring
Annotation Programming Model>>.
[[testcontext-framework]]
@ -1066,15 +1293,17 @@ configuration__ with reasonable defaults that can be overridden through annotati
configuration.
In addition to generic testing infrastructure, the TestContext framework provides
explicit support for JUnit 4 and TestNG in the form of `abstract` support classes. For
JUnit 4, Spring also provides a custom JUnit `Runner` and custom JUnit `Rules` that allow
one to write so-called __POJO test classes__. POJO test classes are not required to
extend a particular class hierarchy.
explicit support for JUnit 4, JUnit Jupiter (a.k.a., JUnit 5), and TestNG. For JUnit 4
and TestNG, Spring provides `abstract` support classes. Furthermore, Spring provides a
custom JUnit `Runner` and custom JUnit `Rules` for _JUnit 4_ as well as a custom
`Extension` for _JUnit Jupiter_ that allow one to write so-called __POJO test classes__.
POJO test classes are not required to extend a particular class hierarchy such as the
`abstract` support classes.
The following section provides an overview of the internals of the TestContext
framework. If you are only interested in _using_ the framework and not necessarily
interested in _extending_ it with your own custom listeners or custom loaders, feel free
to go directly to the configuration (<<testcontext-ctx-management,context management>>,
The following section provides an overview of the internals of the TestContext framework.
If you are only interested in _using_ the framework and not necessarily interested in
_extending_ it with your own custom listeners or custom loaders, feel free to go directly
to the configuration (<<testcontext-ctx-management,context management>>,
<<testcontext-fixture-di,dependency injection>>, <<testcontext-tx,transaction
management>>), <<testcontext-support-classes,support classes>>, and
<<integration-testing-annotations,annotation support>> sections.
@ -1085,14 +1314,14 @@ management>>), <<testcontext-support-classes,support classes>>, and
The core of the framework consists of the `TestContextManager` class and the
`TestContext`, `TestExecutionListener`, and `SmartContextLoader` interfaces. A
`TestContextManager` is created per test class (e.g., for the execution of all test
methods within a single test class in JUnit 4). The `TestContextManager` in turn manages a
`TestContext` that holds the context of the current test. The `TestContextManager` also
updates the state of the `TestContext` as the test progresses and delegates to
`TestExecutionListener` implementations, which instrument the actual test execution by
providing dependency injection, managing transactions, and so on. A `SmartContextLoader`
is responsible for loading an `ApplicationContext` for a given test class. Consult the
javadocs and the Spring test suite for further information and examples of various
implementations.
methods within a single test class in JUnit Jupiter). The `TestContextManager` in turn
manages a `TestContext` that holds the context of the current test. The
`TestContextManager` also updates the state of the `TestContext` as the test progresses
and delegates to `TestExecutionListener` implementations, which instrument the actual
test execution by providing dependency injection, managing transactions, and so on. A
`SmartContextLoader` is responsible for loading an `ApplicationContext` for a given test
class. Consult the javadocs and the Spring test suite for further information and
examples of various implementations.
===== TestContext
`TestContext` encapsulates the context in which a test is executed, agnostic of the
@ -3090,11 +3319,11 @@ transaction method__ or __after transaction method__ is executed at the appropri
[TIP]
====
Any __before methods__ (such as methods annotated with JUnit 4's `@Before`) and any __after
methods__ (such as methods annotated with JUnit 4's `@After`) are executed __within__ a
transaction. In addition, methods annotated with `@BeforeTransaction` or
`@AfterTransaction` are naturally not executed for test methods that are not configured
to run within a transaction.
Any __before methods__ (such as methods annotated with JUnit Jupiter's `@BeforeEach`) and
any __after methods__ (such as methods annotated with JUnit Jupiter's `@AfterEach`) are
executed __within__ a transaction. In addition, methods annotated with
`@BeforeTransaction` or `@AfterTransaction` are naturally not executed for test methods
that are not configured to run within a transaction.
====
[[testcontext-tx-mgr-config]]
@ -3112,7 +3341,7 @@ used to look up a transaction manager in the test's `ApplicationContext`.
[[testcontext-tx-annotation-demo]]
===== Demonstration of all transaction-related annotations
The following JUnit 4 based example displays a fictitious integration testing scenario
The following JUnit 4 based example displays a _fictitious_ integration testing scenario
highlighting all transaction-related annotations. The example is **not** intended to
demonstrate best practices but rather to demonstrate how these annotations can be used.
Consult the <<integration-testing-annotations,annotation support>> section for further