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.DatabaseEnvironment;
31 import org.dbunit.DatabaseProfile;
32 import org.dbunit.IDatabaseTester;
33 import org.dbunit.JdbcDatabaseTester;
34 import org.dbunit.annotation.DbUnitExpected;
35 import org.dbunit.annotation.DbUnitPrep;
36 import org.dbunit.annotation.DbUnitRowCountCheck;
37 import org.dbunit.annotation.DbUnitSetup;
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.database.rowcount.UnexpectedRowCountException;
44 import org.dbunit.operation.DbUnitOperation;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.api.extension.ExtendWith;
47 import org.junit.platform.engine.TestExecutionResult;
48 import org.junit.platform.testkit.engine.EngineTestKit;
49 import org.junit.platform.testkit.engine.Event;
50
51
52
53
54
55
56
57
58
59
60
61
62 @ClearRowCountCheckSystemProperties
63 class DbUnitExtensionRealTesterRowCountCheckIT
64 {
65 private static final String TEST_TABLE = "TEST_TABLE";
66 private static final String EMPTY_TABLE = "EMPTY_TABLE";
67
68 @Test
69 void testAfterTestExecution_setupTeardownPathWithRowCountCheck_piggybacksBaselineAndVerifiesClean()
70 throws Exception
71 {
72 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
73 runSampleExpectingSuccess(environment, SetupTeardownPathSample.class);
74
75 final IDatabaseConnection verifyConnection = environment.getConnection();
76 assertThat(rowCount(verifyConnection, TEST_TABLE))
77 .as("The setup/teardown path with @DbUnitRowCountCheck, driven through a real"
78 + " JdbcDatabaseTester: baseline piggybacks on onSetup()'s connection,"
79 + " verify re-uses that memoized connection, and"
80 + " @DbUnitTearDown(DELETE_ALL) commits - a separate connection sees"
81 + " TEST_TABLE empty.")
82 .isZero();
83 }
84
85 @Test
86 void testAfterTestExecution_prepExpectedPathWithRowCountCheck_verifiesDataAndRowCountsClean()
87 throws Exception
88 {
89 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
90 runSampleExpectingSuccess(environment, PrepExpectedPathSample.class);
91
92 final IDatabaseConnection verifyConnection = environment.getConnection();
93 assertThat(rowCount(verifyConnection, TEST_TABLE))
94 .as("The prep/expected path with @DbUnitRowCountCheck, driven through a real"
95 + " JdbcDatabaseTester: DefaultPrepAndExpectedTestCase captures the"
96 + " baseline and verifies data + row counts on its reusable connection,"
97 + " and @DbUnitTearDown(DELETE_ALL) commits.")
98 .isZero();
99 }
100
101 @Test
102 void testAfterTestExecution_testTableLeftDirtyByAnEarlierTest_stillStartsFromEmptyTables()
103 throws Exception
104 {
105 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
106 try (Statement statement =
107 environment.getConnection().getConnection().createStatement())
108 {
109
110
111
112 statement.execute("INSERT INTO " + TEST_TABLE + " (COLUMN0) VALUES ('stray0')");
113 statement.execute("INSERT INTO " + TEST_TABLE + " (COLUMN0) VALUES ('stray1')");
114 }
115
116 runSampleExpectingSuccess(environment, PrepExpectedPathSample.class);
117
118 assertThat(rowCount(environment.getConnection(), TEST_TABLE))
119 .as("The lifecycle's teardown must leave TEST_TABLE empty, stray rows included.")
120 .isZero();
121 }
122
123 @Test
124 void testAfterTestExecution_rowLeakedThroughARealTester_failsProvingTheCheckActuallyRan()
125 throws Exception
126 {
127 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
128 try
129 {
130 LeakThroughRealTesterSample.databaseTester = newJdbcDatabaseTester(environment);
131
132 final Event failed = EngineTestKit.engine("junit-jupiter")
133 .selectors(selectClass(LeakThroughRealTesterSample.class)).execute()
134 .testEvents().failed().stream().findFirst().orElseThrow(
135 () -> new AssertionError("Expected one failed test event."));
136 final Throwable reported = failed.getRequiredPayload(TestExecutionResult.class)
137 .getThrowable().orElseThrow(
138 () -> new AssertionError("Expected a reported throwable."));
139
140 assertThat(reported)
141 .as("A row left in an unlisted table must fail with"
142 + " UnexpectedRowCountException even when the tester is a real"
143 + " fresh-connection JdbcDatabaseTester - proving the check is"
144 + " genuinely running, not silently no-op because a fresh"
145 + " connection defeated the baseline.")
146 .isInstanceOf(UnexpectedRowCountException.class);
147 } finally
148 {
149 deleteAllRowsQuietly(environment, EMPTY_TABLE);
150 environment.closeConnection();
151 }
152 }
153
154 private void runSampleExpectingSuccess(final DatabaseEnvironment environment,
155 final Class<?> sampleClass) throws Exception
156 {
157 try
158 {
159 environment.deleteAllRows(TEST_TABLE);
160 setSampleTester(sampleClass, newJdbcDatabaseTester(environment));
161
162 EngineTestKit.engine("junit-jupiter").selectors(selectClass(sampleClass)).execute()
163 .testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
164 } finally
165 {
166 deleteAllRowsQuietly(environment, TEST_TABLE);
167 environment.closeConnection();
168 }
169 }
170
171 private void setSampleTester(final Class<?> sampleClass, final IDatabaseTester tester)
172 {
173 if (sampleClass == SetupTeardownPathSample.class)
174 {
175 SetupTeardownPathSample.databaseTester = tester;
176 } else
177 {
178 PrepExpectedPathSample.databaseTester = tester;
179 }
180 }
181
182 private static IDatabaseTester newJdbcDatabaseTester(final DatabaseEnvironment environment)
183 throws Exception
184 {
185 final DatabaseProfile profile = environment.getProfile();
186 return new JdbcDatabaseTester(profile.getDriverClass(), profile.getConnectionUrl(),
187 profile.getUser(), profile.getPassword(), profile.getSchema());
188 }
189
190 private static int rowCount(final IDatabaseConnection connection, final String tableName)
191 throws Exception
192 {
193 try (Statement statement = connection.getConnection().createStatement();
194 ResultSet resultSet =
195 statement.executeQuery("SELECT COUNT(*) FROM " + tableName))
196 {
197 resultSet.next();
198 return resultSet.getInt(1);
199 }
200 }
201
202 private static void deleteAllRowsQuietly(final DatabaseEnvironment environment,
203 final String tableName)
204 {
205 try (Statement statement =
206 environment.getConnection().getConnection().createStatement())
207 {
208 statement.execute("DELETE FROM " + tableName);
209 } catch (final Exception e)
210 {
211
212 }
213 }
214
215 @ExtendWith(DbUnitExtension.class)
216 @ClearRowCountCheckSystemProperties
217 @DbUnitRowCountCheck
218 static class SetupTeardownPathSample
219 {
220 @DbUnitTester
221 static IDatabaseTester databaseTester;
222
223 @Test
224 @DbUnitPrep("annotation-it-prep.xml")
225 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
226 void mutateTheSeededRow(final Connection connection) throws Exception
227 {
228 try (Statement statement = connection.createStatement())
229 {
230 statement.execute("UPDATE " + TEST_TABLE
231 + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
232 }
233 }
234 }
235
236 @ExtendWith(DbUnitExtension.class)
237 @ClearRowCountCheckSystemProperties
238 @DbUnitRowCountCheck
239 static class PrepExpectedPathSample
240 {
241 @DbUnitTester
242 static IDatabaseTester databaseTester;
243
244 @Test
245 @DbUnitPrep("annotation-it-prep.xml")
246 @DbUnitExpected(value = "annotation-it-expected.xml",
247 verify = @DbUnitVerifyTable(value = TEST_TABLE,
248 include = {"COLUMN0", "COLUMN1"}))
249 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
250 void mutateTheSeededRow(final Connection connection) throws Exception
251 {
252 try (Statement statement = connection.createStatement())
253 {
254 statement.execute("UPDATE " + TEST_TABLE
255 + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
256 }
257 }
258 }
259
260 @ExtendWith(DbUnitExtension.class)
261 @ClearRowCountCheckSystemProperties
262 @DbUnitRowCountCheck
263 @DbUnitSetup(operation = DbUnitOperation.NONE)
264 static class LeakThroughRealTesterSample
265 {
266 @DbUnitTester
267 static IDatabaseTester databaseTester;
268
269 @Test
270 void leaveARowInAnUnlistedTable(final Connection connection) throws Exception
271 {
272 try (Statement statement = connection.createStatement())
273 {
274 statement.execute("INSERT INTO " + EMPTY_TABLE + " (COLUMN0) VALUES ('leaked')");
275 }
276 }
277 }
278 }