1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.dbunit.junit.jupiter;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
25
26 import java.sql.Connection;
27 import java.sql.ResultSet;
28 import java.sql.Statement;
29
30 import org.dbunit.DataSourceDatabaseTester;
31 import org.dbunit.DatabaseEnvironment;
32 import org.dbunit.IDatabaseTester;
33 import org.dbunit.annotation.DbUnitConfig;
34 import org.dbunit.annotation.DbUnitExpected;
35 import org.dbunit.annotation.DbUnitPrep;
36 import org.dbunit.annotation.DbUnitProperty;
37 import org.dbunit.annotation.DbUnitRowCountCheck;
38 import org.dbunit.annotation.DbUnitTearDown;
39 import org.dbunit.annotation.DbUnitTester;
40 import org.dbunit.annotation.DbUnitVerifyTable;
41 import org.dbunit.database.IDatabaseConnection;
42 import org.dbunit.database.rowcount.ClearRowCountCheckSystemProperties;
43 import org.dbunit.operation.DbUnitOperation;
44 import org.junit.jupiter.api.BeforeEach;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.api.extension.ExtendWith;
47 import org.junit.platform.testkit.engine.EngineTestKit;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 @ClearRowCountCheckSystemProperties
68 class DbUnitExtensionConnectionBalanceIT
69 {
70 private static final String TEST_TABLE = "TEST_TABLE";
71
72 @Test
73 void testAfterTestExecution_setupTeardownPathNoRowCountCheck_balancedPeakOfTwo()
74 throws Exception
75 {
76 runBalanced(SetupTeardownNoCheckSample.class, 2);
77 }
78
79 @Test
80 void testAfterTestExecution_setupTeardownPathWithRowCountCheck_balancedPeakOfTwo()
81 throws Exception
82 {
83 runBalanced(SetupTeardownWithCheckSample.class, 2);
84 }
85
86 @Test
87 void testAfterTestExecution_prepExpectedPathWithRowCountCheck_balancedPeakOfOne()
88 throws Exception
89 {
90 runBalanced(PrepExpectedWithCheckSample.class, 1);
91 }
92
93 @Test
94 void testAfterTestExecution_injectedConnectionParameterWithRowCountCheck_balancedPeakOfTwo()
95 throws Exception
96 {
97 runBalanced(InjectedConnectionSample.class, 2);
98 }
99
100 @Test
101 void testAfterEach_beforeEachFailsAfterConnectionInjected_closesEveryConnectionOpened()
102 throws Exception
103 {
104 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
105 final CountingDataSource dataSource = new CountingDataSource(environment.getProfile());
106 BalanceSample.databaseTester = new DataSourceDatabaseTester(dataSource,
107 environment.getProfile().getSchema());
108 try
109 {
110 EngineTestKit.engine("junit-jupiter")
111 .selectors(selectClass(FailingBeforeEachSample.class)).execute().testEvents()
112 .assertStatistics(stats -> stats.started(1).failed(1));
113
114 assertThat(dataSource.opened())
115 .as("The failing @BeforeEach must have been handed a connection, or this"
116 + " test proves nothing.")
117 .isPositive();
118 assertThat(dataSource.leaked())
119 .as("A connection injected into a @BeforeEach that then fails must still be"
120 + " closed, because no after-test callback runs once @BeforeEach"
121 + " fails.")
122 .isZero();
123 } finally
124 {
125 environment.closeConnection();
126 }
127 }
128
129 @Test
130 void testBeforeTestExecution_invalidPropertyValueThrowsInTheListener_closesTheTestersConnection()
131 throws Exception
132 {
133 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
134 final CountingDataSource dataSource = new CountingDataSource(environment.getProfile());
135 BalanceSample.databaseTester = new DataSourceDatabaseTester(dataSource,
136 environment.getProfile().getSchema());
137 try
138 {
139 EngineTestKit.engine("junit-jupiter")
140 .selectors(selectClass(InvalidPropertyValueSample.class)).execute()
141 .testEvents().assertStatistics(stats -> stats.started(1).failed(1));
142
143 assertThat(dataSource.opened())
144 .as("onSetup() must have opened a connection, or this test proves nothing.")
145 .isPositive();
146 assertThat(dataSource.leaked())
147 .as("A connection the tester opened must be closed even when the executor's"
148 + " listener throws while applying a @DbUnitProperty value to it.")
149 .isZero();
150 } finally
151 {
152 environment.closeConnection();
153 }
154 }
155
156 @Test
157 void testBeforeTestExecution_invalidPropertyValueOnTheExpectedPath_closesTheTestersConnection()
158 throws Exception
159 {
160 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
161 final CountingDataSource dataSource = new CountingDataSource(environment.getProfile());
162 BalanceSample.databaseTester = new DataSourceDatabaseTester(dataSource,
163 environment.getProfile().getSchema());
164 try
165 {
166 EngineTestKit.engine("junit-jupiter")
167 .selectors(selectClass(InvalidPropertyValueOnExpectedPathSample.class))
168 .execute().testEvents()
169 .assertStatistics(stats -> stats.started(1).failed(1));
170
171 assertThat(dataSource.opened())
172 .as("configureTest() must have opened a connection, or this test proves"
173 + " nothing.")
174 .isPositive();
175 assertThat(dataSource.leaked())
176 .as("A connection the tester opened must be closed even when applying a"
177 + " @DbUnitProperty value to it fails on the prep/expected path.")
178 .isZero();
179 } finally
180 {
181 environment.closeConnection();
182 }
183 }
184
185 @Test
186 void testBeforeTestExecution_expectedFileMissing_closesTheConnectionConfigureTestAcquired()
187 throws Exception
188 {
189 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
190 final CountingDataSource dataSource = new CountingDataSource(environment.getProfile());
191 BalanceSample.databaseTester = new DataSourceDatabaseTester(dataSource,
192 environment.getProfile().getSchema());
193 try
194 {
195 EngineTestKit.engine("junit-jupiter")
196 .selectors(selectClass(MissingExpectedFileSample.class)).execute()
197 .testEvents().assertStatistics(stats -> stats.started(1).failed(1));
198
199 assertThat(dataSource.opened())
200 .as("configureTest() must have opened a connection, or this test proves"
201 + " nothing.")
202 .isPositive();
203 assertThat(dataSource.leaked())
204 .as("A connection configureTest() acquired must be closed when it then fails"
205 + " to load the expected data file, because the runtime skips"
206 + " postTest() for a test that never finished configuring.")
207 .isZero();
208 } finally
209 {
210 environment.closeConnection();
211 }
212 }
213
214 private void runBalanced(final Class<?> sampleClass, final int expectedPeak) throws Exception
215 {
216 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
217 final CountingDataSource dataSource = new CountingDataSource(environment.getProfile());
218 deleteAllRowsQuietly(environment);
219 BalanceSample.databaseTester = new DataSourceDatabaseTester(dataSource,
220 environment.getProfile().getSchema());
221 try
222 {
223 EngineTestKit.engine("junit-jupiter").selectors(selectClass(sampleClass)).execute()
224 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
225
226 assertThat(dataSource.leaked())
227 .as("%s must close every connection it opened.", sampleClass.getSimpleName())
228 .isZero();
229 assertThat(dataSource.peakConcurrent())
230 .as("%s reaches this peak connection concurrency - see the class Javadoc.",
231 sampleClass.getSimpleName())
232 .isEqualTo(expectedPeak);
233 } finally
234 {
235 deleteAllRowsQuietly(environment);
236 environment.closeConnection();
237 }
238 }
239
240 private static void deleteAllRowsQuietly(final DatabaseEnvironment environment)
241 {
242 try (Statement statement =
243 environment.getConnection().getConnection().createStatement())
244 {
245 statement.execute("DELETE FROM " + TEST_TABLE);
246 } catch (final Exception e)
247 {
248
249 }
250 }
251
252 abstract static class BalanceSample
253 {
254 @DbUnitTester
255 static IDatabaseTester databaseTester;
256 }
257
258 @ExtendWith(DbUnitExtension.class)
259 @ClearRowCountCheckSystemProperties
260 static class SetupTeardownNoCheckSample extends BalanceSample
261 {
262 @Test
263 @DbUnitPrep("annotation-it-prep.xml")
264 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
265 void runsTheLifecycle()
266 {
267 }
268 }
269
270 @ExtendWith(DbUnitExtension.class)
271 @ClearRowCountCheckSystemProperties
272 @DbUnitRowCountCheck
273 static class SetupTeardownWithCheckSample extends BalanceSample
274 {
275 @Test
276 @DbUnitPrep("annotation-it-prep.xml")
277 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
278 void runsTheLifecycle()
279 {
280 }
281 }
282
283 @ExtendWith(DbUnitExtension.class)
284 @ClearRowCountCheckSystemProperties
285 @DbUnitRowCountCheck
286 static class PrepExpectedWithCheckSample extends BalanceSample
287 {
288 @Test
289 @DbUnitPrep("annotation-it-prep.xml")
290 @DbUnitExpected(value = "annotation-it-expected.xml",
291 verify = @DbUnitVerifyTable(value = TEST_TABLE,
292 include = {"COLUMN0", "COLUMN1"}))
293 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
294 void mutateTheSeededRow(final Connection connection) throws Exception
295 {
296 try (Statement statement = connection.createStatement())
297 {
298 statement.execute("UPDATE " + TEST_TABLE
299 + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
300 }
301 }
302 }
303
304 @ExtendWith(DbUnitExtension.class)
305 @ClearRowCountCheckSystemProperties
306 @DbUnitRowCountCheck
307 static class InjectedConnectionSample extends BalanceSample
308 {
309 @Test
310 @DbUnitPrep("annotation-it-prep.xml")
311 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
312 void readsThroughTheInjectedConnection(final IDatabaseConnection connection)
313 throws Exception
314 {
315 try (Statement statement = connection.getConnection().createStatement();
316 ResultSet resultSet = statement.executeQuery(
317 "SELECT COUNT(*) FROM " + TEST_TABLE + " WHERE COLUMN0 = 'row0'"))
318 {
319 resultSet.next();
320 assertThat(resultSet.getInt(1))
321 .as("The injected connection must see @DbUnitPrep's seeded row.")
322 .isEqualTo(1);
323 }
324 }
325 }
326
327 @ExtendWith(DbUnitExtension.class)
328 @ClearRowCountCheckSystemProperties
329 static class FailingBeforeEachSample extends BalanceSample
330 {
331 @BeforeEach
332 void failAfterTheConnectionIsInjected(final Connection connection)
333 {
334 throw new IllegalStateException("intentional @BeforeEach failure");
335 }
336
337 @Test
338 @DbUnitPrep("annotation-it-prep.xml")
339 void neverRuns()
340 {
341 }
342 }
343
344 @ExtendWith(DbUnitExtension.class)
345 @ClearRowCountCheckSystemProperties
346 @DbUnitConfig(properties = @DbUnitProperty(name = "batchSize", value = "notANumber"))
347 static class InvalidPropertyValueSample extends BalanceSample
348 {
349 @Test
350 @DbUnitPrep("annotation-it-prep.xml")
351 void neverRuns()
352 {
353 }
354 }
355
356 @ExtendWith(DbUnitExtension.class)
357 @ClearRowCountCheckSystemProperties
358 @DbUnitConfig(properties = @DbUnitProperty(name = "batchSize", value = "notANumber"))
359 static class InvalidPropertyValueOnExpectedPathSample extends BalanceSample
360 {
361 @Test
362 @DbUnitPrep("annotation-it-prep.xml")
363 @DbUnitExpected(value = "annotation-it-expected.xml",
364 verify = @DbUnitVerifyTable(value = TEST_TABLE,
365 include = {"COLUMN0", "COLUMN1"}))
366 void neverRuns()
367 {
368 }
369 }
370
371 @ExtendWith(DbUnitExtension.class)
372 @ClearRowCountCheckSystemProperties
373 static class MissingExpectedFileSample extends BalanceSample
374 {
375 @Test
376 @DbUnitPrep("annotation-it-prep.xml")
377 @DbUnitExpected(value = "no-such-expected-file.xml",
378 verify = @DbUnitVerifyTable(value = TEST_TABLE,
379 include = {"COLUMN0", "COLUMN1"}))
380 void neverRuns()
381 {
382 }
383 }
384 }