Skip to content

Commit 2bcc2af

Browse files
committed
HHH-19912 Add automatic test data cleanup feature to @SessionFactory annotation
1 parent 8a56476 commit 2bcc2af

File tree

4 files changed

+291
-3
lines changed

4 files changed

+291
-3
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.testing.orm.junit;
6+
7+
/**
8+
* Enumeration of when to drop test data automatically.
9+
*
10+
* @author inpink
11+
*/
12+
public enum DropDataTiming {
13+
/**
14+
* Never drop test data automatically
15+
*/
16+
NEVER,
17+
18+
/**
19+
* Drop test data before each test method
20+
*/
21+
BEFORE_EACH,
22+
23+
/**
24+
* Drop test data after each test method
25+
*/
26+
AFTER_EACH,
27+
28+
/**
29+
* Drop test data before all test methods (once per test class)
30+
*/
31+
BEFORE_ALL,
32+
33+
/**
34+
* Drop test data after all test methods (once per test class)
35+
*/
36+
AFTER_ALL
37+
}

hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactory.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
/**
2121
* @author Steve Ebersole
22+
* @author inpink
2223
*/
2324
@Inherited
2425
@Target({ElementType.TYPE, ElementType.METHOD})
@@ -56,4 +57,11 @@
5657
boolean useCollectingStatementInspector() default false;
5758

5859
boolean applyCollectionsInDefaultFetchGroup() default true;
60+
61+
/**
62+
* When to automatically drop test data.
63+
*
64+
* @return the timing for dropping test data
65+
*/
66+
DropDataTiming dropTestData() default DropDataTiming.NEVER;
5967
}

hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/SessionFactoryExtension.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
2323
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.ActionGrouping;
2424
import org.jboss.logging.Logger;
25+
import org.junit.jupiter.api.extension.AfterAllCallback;
26+
import org.junit.jupiter.api.extension.AfterEachCallback;
27+
import org.junit.jupiter.api.extension.BeforeAllCallback;
2528
import org.junit.jupiter.api.extension.BeforeEachCallback;
2629
import org.junit.jupiter.api.extension.ExtensionContext;
2730
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
@@ -43,12 +46,15 @@
4346
* @see DomainModelExtension
4447
*
4548
* @author Steve Ebersole
49+
* @author inpink
4650
*/
4751
public class SessionFactoryExtension
48-
implements TestInstancePostProcessor, BeforeEachCallback, TestExecutionExceptionHandler {
52+
implements TestInstancePostProcessor, BeforeAllCallback, BeforeEachCallback,
53+
AfterEachCallback, AfterAllCallback, TestExecutionExceptionHandler {
4954

5055
private static final Logger log = Logger.getLogger( SessionFactoryExtension.class );
5156
private static final String SESSION_FACTORY_KEY = SessionFactoryScope.class.getName();
57+
private static final String DROP_DATA_TIMING_KEY = "DROP_DATA_TIMING";
5258

5359
/**
5460
* Intended for use from external consumers. Will never create a scope, just
@@ -74,15 +80,19 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex
7480
);
7581

7682
if ( sfAnnRef.isPresent()
77-
|| SessionFactoryProducer.class.isAssignableFrom( context.getRequiredTestClass() ) ) {
83+
|| SessionFactoryProducer.class.isAssignableFrom( context.getRequiredTestClass() ) ) {
7884
final DomainModelScope domainModelScope = DomainModelExtension.getOrCreateDomainModelScope( testInstance, context );
7985
final SessionFactoryScope created = createSessionFactoryScope( testInstance, sfAnnRef, domainModelScope, context );
80-
locateExtensionStore( testInstance, context ).put( SESSION_FACTORY_KEY, created );
86+
final ExtensionContext.Store store = locateExtensionStore( testInstance, context );
87+
store.put( SESSION_FACTORY_KEY, created );
88+
sfAnnRef.ifPresent( sessionFactory -> store.put( DROP_DATA_TIMING_KEY, sessionFactory.dropTestData() ) );
8189
}
8290
}
8391

8492
@Override
8593
public void beforeEach(ExtensionContext context) {
94+
handleDropData(context, DropDataTiming.BEFORE_EACH);
95+
8696
final Optional<SessionFactory> sfAnnRef = AnnotationSupport.findAnnotation(
8797
context.getRequiredTestMethod(),
8898
SessionFactory.class
@@ -97,6 +107,7 @@ public void beforeEach(ExtensionContext context) {
97107
final DomainModelScope domainModelScope = DomainModelExtension.resolveForMethodLevelSessionFactoryScope( context );
98108
final SessionFactoryScope created = createSessionFactoryScope( context.getRequiredTestInstance(), sfAnnRef, domainModelScope, context );
99109
final ExtensionContext.Store extensionStore = locateExtensionStore( context.getRequiredTestInstance(), context );
110+
extensionStore.put( DROP_DATA_TIMING_KEY, sfAnnRef.get().dropTestData() );
100111
extensionStore.put( SESSION_FACTORY_KEY, created );
101112
}
102113

@@ -231,6 +242,38 @@ public void handleTestExecutionException(ExtensionContext context, Throwable thr
231242
throw throwable;
232243
}
233244

245+
@Override
246+
public void beforeAll(ExtensionContext context) throws Exception {
247+
handleDropData(context, DropDataTiming.BEFORE_ALL);
248+
}
249+
250+
@Override
251+
public void afterEach(ExtensionContext context) throws Exception {
252+
handleDropData(context, DropDataTiming.AFTER_EACH);
253+
}
254+
255+
@Override
256+
public void afterAll(ExtensionContext context) throws Exception {
257+
handleDropData(context, DropDataTiming.AFTER_ALL);
258+
}
259+
260+
private void handleDropData(ExtensionContext context, DropDataTiming timing) {
261+
try {
262+
final Object testInstance = context.getRequiredTestInstance();
263+
final ExtensionContext.Store store = locateExtensionStore(testInstance, context);
264+
265+
final DropDataTiming configuredTiming = (DropDataTiming) store.get(DROP_DATA_TIMING_KEY);
266+
if (configuredTiming == timing) {
267+
final SessionFactoryScope scope = findSessionFactoryScope(testInstance, context);
268+
scope.dropData();
269+
log.debugf("Dropped data at timing %s for %s", timing, context.getDisplayName());
270+
}
271+
}
272+
catch (Exception e) {
273+
log.debugf("Failed to drop data at timing %s: %s", timing, e.getMessage());
274+
}
275+
}
276+
234277
private static class SessionFactoryScopeImpl implements SessionFactoryScope, AutoCloseable {
235278
private final DomainModelScope modelScope;
236279
private final SessionFactoryProducer producer;
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.testing.annotations.methods;
6+
7+
import org.hibernate.testing.annotations.AnEntity;
8+
import org.hibernate.testing.orm.junit.DomainModel;
9+
import org.hibernate.testing.orm.junit.DropDataTiming;
10+
import org.hibernate.testing.orm.junit.SessionFactory;
11+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
12+
import org.junit.jupiter.api.MethodOrderer;
13+
import org.junit.jupiter.api.Nested;
14+
import org.junit.jupiter.api.Order;
15+
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.api.TestMethodOrder;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
/**
21+
* Tests for dropTestData timing configuration.
22+
*
23+
* @author inpink
24+
*/
25+
public class DropDataTimingTest {
26+
27+
@Nested
28+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
29+
@DomainModel(annotatedClasses = AnEntity.class)
30+
@SessionFactory(dropTestData = DropDataTiming.AFTER_EACH)
31+
class AfterEachDropDataTests {
32+
33+
@Test
34+
@Order(1)
35+
public void testAfterEachDropData(SessionFactoryScope scope) {
36+
scope.inTransaction(session -> {
37+
AnEntity entity = new AnEntity(1, "After Each");
38+
session.persist(entity);
39+
});
40+
41+
scope.inTransaction(session -> {
42+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
43+
.getSingleResult();
44+
assertThat(count).isEqualTo(1L);
45+
});
46+
}
47+
48+
@Test
49+
@Order(2)
50+
public void verifyAfterEachDropDataCleanup(SessionFactoryScope scope) {
51+
scope.inTransaction(session -> {
52+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
53+
.getSingleResult();
54+
assertThat(count).isEqualTo(0L);
55+
});
56+
}
57+
}
58+
59+
@Nested
60+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
61+
@DomainModel(annotatedClasses = AnEntity.class)
62+
@SessionFactory(dropTestData = DropDataTiming.BEFORE_EACH)
63+
class BeforeEachDropDataTests {
64+
65+
@Test
66+
@Order(1)
67+
public void prepareBeforeEachDropData(SessionFactoryScope scope) {
68+
scope.inTransaction(session -> {
69+
AnEntity entity = new AnEntity(2, "Before Each");
70+
session.persist(entity);
71+
});
72+
}
73+
74+
@Test
75+
@Order(2)
76+
public void testBeforeEachDropData(SessionFactoryScope scope) {
77+
scope.inTransaction(session -> {
78+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
79+
.getSingleResult();
80+
assertThat(count).isEqualTo(0L);
81+
});
82+
}
83+
}
84+
85+
@Nested
86+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
87+
@DomainModel(annotatedClasses = AnEntity.class)
88+
@SessionFactory(dropTestData = DropDataTiming.NEVER)
89+
class NeverDropDataTests {
90+
91+
@Test
92+
@Order(1)
93+
public void prepareNeverDropData(SessionFactoryScope scope) {
94+
scope.inTransaction(session -> {
95+
AnEntity entity = new AnEntity(3, "Never");
96+
session.persist(entity);
97+
});
98+
}
99+
100+
@Test
101+
@Order(2)
102+
public void testNeverDropData(SessionFactoryScope scope) {
103+
scope.inTransaction(session -> {
104+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
105+
.getSingleResult();
106+
assertThat(count).isGreaterThan(0L);
107+
});
108+
109+
}
110+
111+
@Test
112+
@Order(3)
113+
public void verifyNeverDropDataStillPresent(SessionFactoryScope scope) {
114+
scope.inTransaction(session -> {
115+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
116+
.getSingleResult();
117+
assertThat(count).isGreaterThan(0L);
118+
});
119+
120+
scope.dropData();
121+
}
122+
}
123+
124+
@Nested
125+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
126+
@DomainModel(annotatedClasses = AnEntity.class)
127+
class MethodLevelDropDataTests {
128+
129+
@Test
130+
@Order(1)
131+
@SessionFactory(dropTestData = DropDataTiming.AFTER_EACH)
132+
public void methodLevelAfterEach(SessionFactoryScope scope) {
133+
scope.inTransaction(session -> {
134+
AnEntity entity = new AnEntity(10, "Method After Each");
135+
session.persist(entity);
136+
});
137+
138+
scope.inTransaction(session -> {
139+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
140+
.getSingleResult();
141+
assertThat(count).isEqualTo(1L);
142+
});
143+
}
144+
145+
@Test
146+
@Order(2)
147+
@SessionFactory(dropTestData = DropDataTiming.AFTER_EACH)
148+
public void methodLevelAfterEachCleanup(SessionFactoryScope scope) {
149+
scope.inTransaction(session -> {
150+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
151+
.getSingleResult();
152+
assertThat(count).isEqualTo(0L);
153+
});
154+
}
155+
156+
@Test
157+
@Order(3)
158+
@SessionFactory(dropTestData = DropDataTiming.BEFORE_EACH)
159+
public void methodLevelBeforeEachInsert(SessionFactoryScope scope) {
160+
scope.inTransaction(session -> {
161+
AnEntity entity = new AnEntity(11, "Method Before Each");
162+
session.persist(entity);
163+
});
164+
}
165+
166+
@Test
167+
@Order(4)
168+
@SessionFactory(dropTestData = DropDataTiming.BEFORE_EACH)
169+
public void methodLevelBeforeEachVerify(SessionFactoryScope scope) {
170+
scope.inTransaction(session -> {
171+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
172+
.getSingleResult();
173+
assertThat(count).isEqualTo(0L);
174+
});
175+
}
176+
177+
@Test
178+
@Order(5)
179+
@SessionFactory(dropTestData = DropDataTiming.NEVER)
180+
public void methodLevelNeverInsert(SessionFactoryScope scope) {
181+
scope.inTransaction(session -> {
182+
AnEntity entity = new AnEntity(12, "Method Never");
183+
session.persist(entity);
184+
});
185+
}
186+
187+
@Test
188+
@Order(6)
189+
@SessionFactory(dropTestData = DropDataTiming.NEVER)
190+
public void methodLevelNeverVerify(SessionFactoryScope scope) {
191+
scope.inTransaction(session -> {
192+
Long count = session.createQuery("select count(e) from AnEntity e", Long.class)
193+
.getSingleResult();
194+
assertThat(count).isEqualTo(0L);
195+
});
196+
197+
scope.dropData();
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)