Skip to content

Commit 20c901f

Browse files
committed
Document JUnit 5 extensions
1 parent a82c216 commit 20c901f

File tree

1 file changed

+183
-2
lines changed

1 file changed

+183
-2
lines changed

Diff for: test-case-guide.adoc

+183-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,188 @@ There are a number of tenants that make up a good test case as opposed to a poor
1313
* (V)erifiable - The test should actually reproduce the problem being reported.
1414

1515

16-
== Test templates
16+
[[junit5]]
17+
== JUnit 5 extensions
18+
19+
JUnit 5 offers better support for integration, compared to JUnit 4, via https://junit.org/junit5/docs/current/user-guide/#extensions[extensions]. Hibernate builds on those concepts in its `hibernate-testing` module allowing set up of test fixtures using annotations. The following sections describe the Hibernate extensions.
20+
21+
NOTE: The extensions exist in the `org.hibernate.testing.orm.junit` package, as opposed to the older `org.hibernate.testing.junit4` package used with JUnit 4.
22+
23+
24+
[[junit5-service-registry]]
25+
=== ServiceRegistryExtension
26+
27+
Manages a `ServiceRegistry` as part of the test lifecycle. 2 in fact, depending on the annotation(s) used.
28+
29+
`@BootstrapServiceRegistry`:: configures Hibernate's bootstrap `BootstrapServiceRegistry` which manages class-loading, etc. `@BootstrapServiceRegistry` is used to provide Java services and Hibernate `Integrator` implementations for the test.
30+
`@ServiceRegistry`:: configures Hibernate's standard `StandardServiceRegistry`. `@ServiceRegistry` is used to provide settings, contributors, services, etc.
31+
32+
Also exposes `ServiceRegistryScope` via JUnit 5 `ParameterResolver`. `ServiceRegistryScope` allows
33+
access to the managed `ServiceRegistry` from tests and callbacks.
34+
35+
```
36+
@BootstrapServiceRegistry(
37+
javaServices=@JavaServices(
38+
role=TypeContributions.class,
39+
impls=CustomTypeContributions.class
40+
),
41+
...
42+
)
43+
@ServiceRegistry(
44+
settings=@Setting(
45+
name="hibernate.show_sql",
46+
value="true"
47+
),
48+
services=@Service(
49+
role=ConnectionProvider.class,
50+
impl=CustomConnectionProvider.class
51+
),
52+
...
53+
)
54+
class TheTest {
55+
@Test void testIt(ServiceRegistryScope scope) {
56+
StandardServiceRegistry reg = scope.getRegistry();
57+
...
58+
}
59+
}
60+
```
61+
62+
63+
[[junit5-domain-model]]
64+
=== DomainModelExtension
65+
66+
Manages the domain model for the test as part of its lifecycle.
67+
68+
`@DomainModel`:: defines the sources of the domain model used in the test - type contributions, managed classes, XML mappings, etc.
69+
70+
If available, this extension uses the `ServiceRegistry` instances available from <<junit5-service-registry>>.
71+
72+
Exposes `DomainModelScope` via JUnit5 `ParameterResolver`, allowing access to details about the domain model from the `org.hibernate.mapping` "boot model".
73+
74+
75+
```
76+
@DomainModel(
77+
standardDomainModels=StandardDomainModel.ANIMAL,
78+
annotatedClasses={Entity1.class, Entity2.class},
79+
xmlMappings="resource/path/to/my-mapping.xml",
80+
...
81+
)
82+
class TheTest {
83+
@Test void testIt(DomainModelScope scope) {
84+
MetadataImplementor meta = scope.getDomainModel();
85+
...
86+
87+
PersistentClass entityMapping = scope.getEntityBinding(Entity1.class);
88+
...
89+
90+
scope.withHierarchy(Entity1.class, (entityMapping) -> {
91+
...
92+
}
93+
}
94+
}
95+
```
96+
97+
98+
=== SessionFactoryExtension
99+
100+
Manages a Hibernate `SessionFactory` as part of the test lifecycle.
101+
102+
`@SessionFactory`:: is used to configure the runtime aspects of the `SessionFactory` fixture.
103+
104+
If available, uses the `ServiceRegistry` instances available from <<junit5-service-registry>> as well
105+
as the domain model defined by <<junit5-domain-model>>.
106+
107+
Exposes `SessionFactoryScope` via JUnit5 `ParameterResolver`.
108+
109+
```
110+
@SessionFactory(
111+
generateStatistics=true,
112+
exportSchema=true,
113+
useCollectingStatementInspector=true,
114+
...
115+
)
116+
class TheTest {
117+
@Test void testIt(SessionFactoryScope scope) {
118+
SQLStatementInspector sqlCollector = scope.getCollectingStatementInspector();
119+
sqlCollector.clear();
120+
121+
scope.inTransaction( (session) -> {
122+
...
123+
assertThat(sqlCollector.getSqlQueries()).isEmpty();
124+
} );
125+
126+
Entity1 e = scope.fromTransaction( (session) -> {
127+
Entity1 it = session.find(Entity1.class, id);
128+
...
129+
return it;
130+
} );
131+
}
132+
}
133+
```
134+
135+
=== DialectFilterExtension
136+
137+
Allows filtering tests based on Dialect used. Implemented as a JUnit `ExecutionCondition` which is used to dynamically determine whether a test should be run. Used in conjunction with:
138+
139+
`@RequiresDialect`:: says to only run this test for the given Dialect(s).
140+
`@SkipForDialect`:: says to skip this test for the given Dialect(s).
141+
142+
=== ExpectedExceptionExtension
143+
144+
Used with `@ExpectedException` to allow testing that an excepted exception occurs as the "success" condition.
145+
146+
```
147+
@DomainModel(...)
148+
@SessionFactory(...)
149+
class TheTest {
150+
@Test
151+
@ExpectedException(UnknownEntityTypeException.class)
152+
void testIt(SessionFactoryScope) {
153+
scope.inTransaction( (session) -> {
154+
// Should fail as MyEmbeddable is not an entity
155+
session.find(MyEmbeddable.class, 1);
156+
} );
157+
}
158+
}
159+
```
160+
161+
162+
=== FailureExpectedExtension
163+
164+
Used with `@FailureExpected` to indicate that a test is (currently) expected to fail. You might use this, e.g., for a test that is the reproducer for a bug report before working on it. It basically just flips the success/failure condition. In fact, a test marked with `@FailureExpected` will be marked a failure if it succeeds.
165+
166+
```
167+
@Test
168+
@JiraKey("HHH-123456789")
169+
@FailureExpected
170+
void bugReproducer(...) {...}
171+
```
172+
173+
174+
=== LoggingInspectionsExtension and MessageKeyInspectionExtension
175+
176+
Both are used for testing log messages.
177+
178+
`@LoggingInspections`:: used to watch more than one "message key".
179+
`MessageKeyInspection`:: used to watch a single "message key".
180+
181+
182+
=== EntityManagerExtension
183+
184+
Used in conjunction with `@Jpa` to build tests with an `EntityManagerFactory` fixture.
185+
186+
Since Hibernate's `SessionFactory` *is a* `EntityManagerFactory`, `@BootstrapServiceRegistry`, `@ServiceRegistry`, `@DomainModel` and `@SessionFactory` can also be used to perform tests with a (`SessionFactory` as a) `EntityManagerFactory` fixture.
187+
188+
The distinction with `@Jpa` is that `EntityManagerExtension` uses the JPA-defined bootstrap APIs. How the
189+
`SessionFactory` is built is the difference.
190+
191+
192+
== JUnit 4
193+
194+
Historically, Hibernate used JUnit 4 for its test suite. Since the release of https://junit.org/junit5/[JUnit 5], we've moved to using the testing approach outlined in <<junit5>>. However, many existing tests still use the legacy JUnit 4 based infrastructure (boilerplate) based on "test templates".
195+
196+
197+
=== Test templates
17198

18199
The Hibernate team maintains a set of "test templates" intended to help developers write tests. These test templates are maintained in GitHub @ https://github.com/hibernate/hibernate-test-case-templates/tree/main/orm[hibernate-test-case-templates]
19200

@@ -22,7 +203,7 @@ The Hibernate team maintains a set of "test templates" intended to help develope
22203

23204
NOTE: the test templates are generally not a good starting point for problems building the SessionFactory/EntityManager. In JUnit terms they manage the SessionFactory/EntityManager as set-up and teardown constructs._
24205

25-
== Annotations
206+
=== Annotations
26207

27208
When using "test templates" you can annotate a single test or a whole test class with one of the following annotations:
28209

0 commit comments

Comments
 (0)