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 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.dbunit.DatabaseEnvironment;
33 import org.dbunit.DatabaseProfile;
34 import org.dbunit.DatabaseTesterFactory;
35 import org.dbunit.DefaultDatabaseTester;
36 import org.dbunit.DefaultPrepAndExpectedTestCase;
37 import org.dbunit.IDatabaseTester;
38 import org.dbunit.IOperationListener;
39 import org.dbunit.JdbcDatabaseTester;
40 import org.dbunit.PrepAndExpectedTestCase;
41 import org.dbunit.annotation.DbUnitConfig;
42 import org.dbunit.annotation.DbUnitExpected;
43 import org.dbunit.annotation.DbUnitPrep;
44 import org.dbunit.annotation.DbUnitSetup;
45 import org.dbunit.annotation.DbUnitTearDown;
46 import org.dbunit.annotation.DbUnitTestCase;
47 import org.dbunit.annotation.DbUnitTester;
48 import org.dbunit.annotation.DbUnitVerifyTable;
49 import org.dbunit.database.CachingConnectionProvider;
50 import org.dbunit.database.IDatabaseConnection;
51 import org.dbunit.operation.DbUnitOperation;
52 import org.junit.jupiter.api.Test;
53 import org.junit.jupiter.api.extension.ExtendWith;
54 import org.junit.platform.testkit.engine.EngineTestKit;
55
56
57
58
59
60
61
62
63
64
65
66 class DbUnitExtensionAnnotationIT
67 {
68 private static final String TEST_TABLE = "TEST_TABLE";
69 private static final String PK_TABLE = "PK_TABLE";
70
71 @Test
72 void testAfterTestExecution_prepMutateExpected_verifiesAndCleansUp() throws Exception
73 {
74 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
75 try
76 {
77 final IDatabaseConnection connection = environment.getConnection();
78 PrepExpectedSample.databaseTester = new DefaultDatabaseTester(connection);
79 PrepExpectedSample.databaseTester
80 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
81
82 EngineTestKit.engine("junit-jupiter")
83 .selectors(selectClass(PrepExpectedSample.class)).execute().testEvents()
84 .assertStatistics(stats -> stats.started(1).succeeded(1));
85
86
87
88 final IDatabaseConnection verifyConnection = environment.getConnection();
89 assertThat(rowCount(verifyConnection, TEST_TABLE))
90 .as("@DbUnitTearDown(operation = DELETE_ALL) must have cleaned up.")
91 .isZero();
92 } finally
93 {
94 deleteAllRowsQuietly(environment, TEST_TABLE);
95 environment.closeConnection();
96 }
97 }
98
99 @Test
100 void testAfterTestExecution_jsonPrepAndExpectedFiles_verifiesAndCleansUp() throws Exception
101 {
102 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
103 try
104 {
105 final IDatabaseConnection connection = environment.getConnection();
106 JsonPrepExpectedSample.databaseTester = new DefaultDatabaseTester(connection);
107 JsonPrepExpectedSample.databaseTester
108 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
109
110 EngineTestKit.engine("junit-jupiter")
111 .selectors(selectClass(JsonPrepExpectedSample.class)).execute().testEvents()
112 .assertStatistics(stats -> stats.started(1).succeeded(1));
113
114 final IDatabaseConnection verifyConnection = environment.getConnection();
115 assertThat(rowCount(verifyConnection, TEST_TABLE))
116 .as("The prep/expected path must load a JSON @DbUnitPrep and @DbUnitExpected"
117 + " file through the default FileExtensionDataFileLoader (dispatched"
118 + " by the .json extension to JsonDataFileLoader), verify, and run"
119 + " @DbUnitTearDown(DELETE_ALL) - end to end, the same as flat XML.")
120 .isZero();
121 } finally
122 {
123 deleteAllRowsQuietly(environment, TEST_TABLE);
124 environment.closeConnection();
125 }
126 }
127
128 @Test
129 void testBeforeTestExecution_classLevelSetupOperation_survivesMethodLevelPrep()
130 throws Exception
131 {
132 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
133 try
134 {
135 final IDatabaseConnection connection = environment.getConnection();
136 ClassLevelOperationSample.databaseTester = new DefaultDatabaseTester(connection);
137 ClassLevelOperationSample.databaseTester
138 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
139 try (Statement statement = connection.getConnection().createStatement())
140 {
141 statement.execute("INSERT INTO " + PK_TABLE
142 + " (PK0, PK1, PK2, NORMAL0) VALUES (997, 997, 997, 'preexisting')");
143 }
144
145 EngineTestKit.engine("junit-jupiter")
146 .selectors(selectClass(ClassLevelOperationSample.class)).execute()
147 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
148
149 assertThat(rowCountForPks(connection, 997, 998))
150 .as("REFRESH (an upsert) must not have wiped the pre-existing row (PK 997)"
151 + " the way CLEAN_INSERT would have; the class-level @DbUnitSetup"
152 + " operation must have survived the method's own @DbUnitPrep"
153 + " (which seeds PK 998).")
154 .isEqualTo(2);
155 } finally
156 {
157 deletePksQuietly(environment, 997, 998);
158 environment.closeConnection();
159 }
160 }
161
162 @Test
163 void testBeforeTestExecution_expectedPathClassLevelSetupOperation_survivesMethodLevelPrep()
164 throws Exception
165 {
166 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
167 try
168 {
169 final IDatabaseConnection connection = environment.getConnection();
170 ExpectedPathClassLevelOperationSample.databaseTester =
171 new DefaultDatabaseTester(connection);
172 ExpectedPathClassLevelOperationSample.databaseTester
173 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
174 try (Statement statement = connection.getConnection().createStatement())
175 {
176 statement.execute("INSERT INTO " + PK_TABLE
177 + " (PK0, PK1, PK2, NORMAL0) VALUES (895, 895, 895, 'preexisting')");
178 }
179
180 EngineTestKit.engine("junit-jupiter")
181 .selectors(selectClass(ExpectedPathClassLevelOperationSample.class)).execute()
182 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
183
184 assertThat(rowCountForPks(connection, 895, 998))
185 .as("REFRESH (an upsert) must not have wiped the pre-existing row (PK 895)"
186 + " the way CLEAN_INSERT would have; the class-level @DbUnitSetup"
187 + " operation must apply on the @DbUnitExpected prep/expected path"
188 + " too, the same as it already does on the setup/teardown path.")
189 .isEqualTo(2);
190 } finally
191 {
192 deletePksQuietly(environment, 895, 998);
193 environment.closeConnection();
194 }
195 }
196
197 @Test
198 void testAfterTestExecution_injectedTestCaseAndFactoryResolvedTester_verifiesAndCleansUp()
199 throws Exception
200 {
201 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
202 try
203 {
204 EngineTestKit.engine("junit-jupiter")
205 .selectors(selectClass(InjectedTestCaseSample.class)).execute().testEvents()
206 .assertStatistics(stats -> stats.started(1).succeeded(1));
207
208
209
210 final IDatabaseConnection verifyConnection = environment.getConnection();
211 assertThat(rowCount(verifyConnection, TEST_TABLE))
212 .as("@DbUnitTearDown(operation = DELETE_ALL), driven through the"
213 + " @DbUnitTestCase-injected PrepAndExpectedTestCase and a"
214 + " databaseTesterFactory-resolved tester, must have cleaned up.")
215 .isZero();
216 } finally
217 {
218 deleteAllRowsQuietly(environment, TEST_TABLE);
219 environment.closeConnection();
220 }
221 }
222
223 @Test
224 void testAfterTestExecution_realJdbcDatabaseTesterExpectedPath_verifiesAndCleansUp()
225 throws Exception
226 {
227 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
228 try
229 {
230 JdbcTesterExpectedPathSample.databaseTester = newJdbcDatabaseTester(environment);
231
232 EngineTestKit.engine("junit-jupiter")
233 .selectors(selectClass(JdbcTesterExpectedPathSample.class)).execute()
234 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
235
236 final IDatabaseConnection verifyConnection = environment.getConnection();
237 assertThat(rowCount(verifyConnection, TEST_TABLE))
238 .as("The prep/expected path driven through a real JdbcDatabaseTester - a"
239 + " fresh physical connection per getConnection() call, its default"
240 + " DefaultOperationListener wrapped by the extension, not a"
241 + " NO_OP-listener DefaultDatabaseTester as the samples above use -"
242 + " must still run setup, verify (against an injected Connection's"
243 + " mutation), and @DbUnitTearDown(DELETE_ALL) end to end.")
244 .isZero();
245 } finally
246 {
247 deleteAllRowsQuietly(environment, TEST_TABLE);
248 environment.closeConnection();
249 }
250 }
251
252 @Test
253 void testAfterTestExecution_realJdbcDatabaseTesterSetupTeardownPath_setsUpAndTearsDown()
254 throws Exception
255 {
256 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
257 try
258 {
259 JdbcTesterSetupTeardownPathSample.databaseTester = newJdbcDatabaseTester(environment);
260
261 EngineTestKit.engine("junit-jupiter")
262 .selectors(selectClass(JdbcTesterSetupTeardownPathSample.class)).execute()
263 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
264
265 final IDatabaseConnection verifyConnection = environment.getConnection();
266 assertThat(rowCount(verifyConnection, TEST_TABLE))
267 .as("The setup/teardown path driven through a real JdbcDatabaseTester - where the"
268 + " extension's ExecutorOperationListener wraps the tester's own"
269 + " DefaultOperationListener, the row count baseline piggybacks on"
270 + " onSetup()'s connection, and onTearDown() opens a different one -"
271 + " must still apply @DbUnitPrep and @DbUnitTearDown(DELETE_ALL) end"
272 + " to end.")
273 .isZero();
274 } finally
275 {
276 deleteAllRowsQuietly(environment, TEST_TABLE);
277 environment.closeConnection();
278 }
279 }
280
281 @Test
282 void testAfterTestExecution_cachingProviderAndCloseConnectionAfterTestFalse_reusesConnectionAcrossMethods()
283 throws Exception
284 {
285 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
286 final DatabaseProfile profile = environment.getProfile();
287 final CachingConnectionProvider provider = new CachingConnectionProvider();
288 CachingProviderSample.connectionsSeen.clear();
289 try
290 {
291 CachingProviderSample.databaseTester = new JdbcDatabaseTester(
292 profile.getDriverClass(), profile.getConnectionUrl(), profile.getUser(),
293 profile.getPassword(), profile.getSchema(), provider);
294
295 EngineTestKit.engine("junit-jupiter")
296 .selectors(selectClass(CachingProviderSample.class)).execute().testEvents()
297 .assertStatistics(stats -> stats.started(2).succeeded(2));
298
299 assertThat(CachingProviderSample.connectionsSeen)
300 .as("@DbUnitConfig(closeConnectionAfterTest = false) with a"
301 + " CachingConnectionProvider must hand both test methods the same"
302 + " physical Connection - the extension must not close the cached"
303 + " connection after the first test.")
304 .hasSize(2)
305 .satisfies(seen -> assertThat(seen.get(0)).isSameAs(seen.get(1)));
306 } finally
307 {
308 deleteAllRowsQuietly(environment, TEST_TABLE);
309 provider.close();
310 environment.closeConnection();
311 }
312 }
313
314 private static IDatabaseTester newJdbcDatabaseTester(final DatabaseEnvironment environment)
315 throws Exception
316 {
317 final DatabaseProfile profile = environment.getProfile();
318 return new JdbcDatabaseTester(profile.getDriverClass(), profile.getConnectionUrl(),
319 profile.getUser(), profile.getPassword(), profile.getSchema());
320 }
321
322 private static int rowCount(final IDatabaseConnection connection, final String tableName)
323 throws Exception
324 {
325 try (Statement statement = connection.getConnection().createStatement();
326 ResultSet resultSet =
327 statement.executeQuery("SELECT COUNT(*) FROM " + tableName))
328 {
329 resultSet.next();
330 return resultSet.getInt(1);
331 }
332 }
333
334 private static int rowCountForPks(final IDatabaseConnection connection, final int... pk0s)
335 throws Exception
336 {
337 final String inList = pkInList(pk0s);
338 try (Statement statement = connection.getConnection().createStatement();
339 ResultSet resultSet = statement.executeQuery(
340 "SELECT COUNT(*) FROM " + PK_TABLE + " WHERE PK0 IN (" + inList + ")"))
341 {
342 resultSet.next();
343 return resultSet.getInt(1);
344 }
345 }
346
347 private static String pkInList(final int... pk0s)
348 {
349 final StringBuilder inList = new StringBuilder();
350 for (int i = 0; i < pk0s.length; i++)
351 {
352 if (i > 0)
353 {
354 inList.append(',');
355 }
356 inList.append(pk0s[i]);
357 }
358 return inList.toString();
359 }
360
361 private static void deleteAllRowsQuietly(final DatabaseEnvironment environment,
362 final String tableName)
363 {
364 try (Statement statement =
365 environment.getConnection().getConnection().createStatement())
366 {
367 statement.execute("DELETE FROM " + tableName);
368 } catch (final Exception e)
369 {
370
371 }
372 }
373
374 private static void deletePksQuietly(final DatabaseEnvironment environment,
375 final int... pk0s)
376 {
377 final String inList = pkInList(pk0s);
378 try (Statement statement =
379 environment.getConnection().getConnection().createStatement())
380 {
381 statement.execute("DELETE FROM " + PK_TABLE + " WHERE PK0 IN (" + inList + ")");
382 } catch (final Exception e)
383 {
384
385 }
386 }
387
388 @ExtendWith(DbUnitExtension.class)
389 static class PrepExpectedSample
390 {
391 @DbUnitTester
392 static IDatabaseTester databaseTester;
393
394 @Test
395 @DbUnitPrep("annotation-it-prep.xml")
396 @DbUnitExpected(value = "annotation-it-expected.xml",
397 verify = @DbUnitVerifyTable(value = TEST_TABLE,
398 include = {"COLUMN0", "COLUMN1"}))
399 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
400 void testWithdraw_sufficientBalance_decrementsBalance() throws Exception
401 {
402 try (Statement statement =
403 databaseTester.getConnection().getConnection().createStatement())
404 {
405 statement.execute(
406 "UPDATE " + TEST_TABLE + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
407 }
408 }
409 }
410
411 @ExtendWith(DbUnitExtension.class)
412 static class JsonPrepExpectedSample
413 {
414 @DbUnitTester
415 static IDatabaseTester databaseTester;
416
417 @Test
418 @DbUnitPrep("annotation-it-prep.json")
419 @DbUnitExpected(value = "annotation-it-expected.json",
420 verify = @DbUnitVerifyTable(value = TEST_TABLE,
421 include = {"COLUMN0", "COLUMN1"}))
422 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
423 void testWithdraw_sufficientBalance_decrementsBalance() throws Exception
424 {
425 try (Statement statement =
426 databaseTester.getConnection().getConnection().createStatement())
427 {
428 statement.execute(
429 "UPDATE " + TEST_TABLE + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
430 }
431 }
432 }
433
434 @ExtendWith(DbUnitExtension.class)
435 static class JdbcTesterExpectedPathSample
436 {
437 @DbUnitTester
438 static IDatabaseTester databaseTester;
439
440 @Test
441 @DbUnitPrep("annotation-it-prep.xml")
442 @DbUnitExpected(value = "annotation-it-expected.xml",
443 verify = @DbUnitVerifyTable(value = TEST_TABLE,
444 include = {"COLUMN0", "COLUMN1"}))
445 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
446 void testUpdate_throughInjectedConnection_verifiesAndCleansUp(
447 final Connection connection) throws Exception
448 {
449 try (Statement statement = connection.createStatement())
450 {
451 statement.execute(
452 "UPDATE " + TEST_TABLE + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
453 }
454 }
455 }
456
457 @ExtendWith(DbUnitExtension.class)
458 static class JdbcTesterSetupTeardownPathSample
459 {
460 @DbUnitTester
461 static IDatabaseTester databaseTester;
462
463 @Test
464 @DbUnitPrep("annotation-it-prep.xml")
465 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
466 void testPrepSeedsRow_visibleThroughInjectedConnection(final Connection connection)
467 throws Exception
468 {
469 try (Statement statement = connection.createStatement();
470 ResultSet resultSet = statement.executeQuery(
471 "SELECT COUNT(*) FROM " + TEST_TABLE + " WHERE COLUMN0 = 'row0'"))
472 {
473 resultSet.next();
474 assertThat(resultSet.getInt(1))
475 .as("@DbUnitPrep's CLEAN_INSERT must have seeded the row on the same"
476 + " connection the injected parameter hands back, before the"
477 + " test method body runs.")
478 .isEqualTo(1);
479 }
480 }
481 }
482
483 @ExtendWith(DbUnitExtension.class)
484 @DbUnitConfig(closeConnectionAfterTest = false)
485 static class CachingProviderSample
486 {
487 static final List<Connection> connectionsSeen = new ArrayList<>();
488
489 @DbUnitTester
490 static IDatabaseTester databaseTester;
491
492 @Test
493 @DbUnitPrep("annotation-it-prep.xml")
494 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
495 void testMethodOne(final Connection connection)
496 {
497 connectionsSeen.add(connection);
498 }
499
500 @Test
501 @DbUnitPrep("annotation-it-prep.xml")
502 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
503 void testMethodTwo(final Connection connection)
504 {
505 connectionsSeen.add(connection);
506 }
507 }
508
509 @ExtendWith(DbUnitExtension.class)
510
511
512 @DbUnitConfig(closeConnectionAfterTest = false)
513 @DbUnitSetup(operation = DbUnitOperation.REFRESH)
514 static class ClassLevelOperationSample
515 {
516 @DbUnitTester
517 static IDatabaseTester databaseTester;
518
519 @Test
520 @DbUnitPrep("annotation-it-pk-prep.xml")
521 void testPrepDeclaredOnMethod_classLevelSetupOperationAlsoDeclared_bothApply()
522 {
523 }
524 }
525
526 @ExtendWith(DbUnitExtension.class)
527
528
529 @DbUnitConfig(closeConnectionAfterTest = false)
530 @DbUnitSetup(operation = DbUnitOperation.REFRESH)
531 static class ExpectedPathClassLevelOperationSample
532 {
533 @DbUnitTester
534 static IDatabaseTester databaseTester;
535
536
537
538
539
540 @Test
541 @DbUnitPrep("annotation-it-pk-prep.xml")
542 @DbUnitExpected
543 void testPrepDeclaredOnMethod_expectedPathWithClassLevelSetupOperation_bothApply()
544 {
545 }
546 }
547
548 @ExtendWith(DbUnitExtension.class)
549 @DbUnitConfig(databaseTesterFactory = InjectedTestCaseSample.Factory.class)
550 static class InjectedTestCaseSample
551 {
552
553
554 @DbUnitTestCase
555 static final PrepAndExpectedTestCase testCase = new DefaultPrepAndExpectedTestCase();
556
557 @Test
558 @DbUnitPrep("annotation-it-prep.xml")
559 @DbUnitExpected(value = "annotation-it-expected.xml",
560 verify = @DbUnitVerifyTable(value = TEST_TABLE,
561 include = {"COLUMN0", "COLUMN1"}))
562 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
563 void testWithdraw_sufficientBalance_decrementsBalance() throws Exception
564 {
565 final IDatabaseConnection connection = testCase.getDatabaseTester().getConnection();
566 try (Statement statement = connection.getConnection().createStatement())
567 {
568 statement.execute(
569 "UPDATE " + TEST_TABLE + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
570 }
571 }
572
573 static class Factory implements DatabaseTesterFactory
574 {
575 @Override
576 public IDatabaseTester getDatabaseTester() throws Exception
577 {
578 final IDatabaseTester tester = new DefaultDatabaseTester(
579 DatabaseEnvironment.getInstance().getConnection());
580 tester.setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
581 return tester;
582 }
583 }
584 }
585 }