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