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.DefaultPrepAndExpectedTestCase;
36 import org.dbunit.IDatabaseTester;
37 import org.dbunit.JdbcDatabaseTester;
38 import org.dbunit.PrepAndExpectedTestCase;
39 import org.dbunit.annotation.DbUnitConfig;
40 import org.dbunit.annotation.DbUnitExpected;
41 import org.dbunit.annotation.DbUnitPrep;
42 import org.dbunit.annotation.DbUnitRowCountCheck;
43 import org.dbunit.annotation.DbUnitTearDown;
44 import org.dbunit.annotation.DbUnitTestCase;
45 import org.dbunit.annotation.DbUnitVerifyTable;
46 import org.dbunit.database.CachingConnectionProvider;
47 import org.dbunit.database.IDatabaseConnection;
48 import org.dbunit.database.rowcount.ClearRowCountCheckSystemProperties;
49 import org.dbunit.operation.DbUnitOperation;
50 import org.junit.jupiter.api.AfterEach;
51 import org.junit.jupiter.api.RepeatedTest;
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
67
68
69
70
71
72
73
74 class DbUnitExtensionConnectionReuseIT
75 {
76 private static final String TEST_TABLE = "TEST_TABLE";
77
78 @Test
79 void testAfterTestExecution_reusedInjectedTestCaseWhoseCachedConnectionDiesMidRun_reacquiresAndKeepsPassing()
80 throws Exception
81 {
82 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
83 final CachingConnectionProvider provider = new CachingConnectionProvider();
84 deleteAllRowsQuietly(environment);
85 ReusedInstanceSample.profile = environment.getProfile();
86 ReusedInstanceSample.provider = provider;
87 ReusedInstanceSample.reset();
88 try
89 {
90 EngineTestKit.engine("junit-jupiter")
91 .selectors(selectClass(ReusedInstanceSample.class)).execute().testEvents()
92 .assertStatistics(stats -> stats.started(5).succeeded(5));
93
94 final List<Connection> seen = ReusedInstanceSample.rawConnectionsSeen;
95 assertThat(seen)
96 .as("Every one of the 5 repetitions must have run, including the three"
97 + " after the pinned connection was force-closed between #2 and"
98 + " #3 - proving the annotation path re-acquires a dropped"
99 + " connection (#962) rather than reusing the dead one or failing"
100 + " in preTest()/setupData().")
101 .hasSize(5);
102 assertThat(seen.subList(0, 2))
103 .as("Repetitions before the kill share the one connection the"
104 + " CachingConnectionProvider cached and closeConnectionAfterTest=false"
105 + " pinned.")
106 .containsOnly(seen.get(0));
107 assertThat(seen.subList(2, 5))
108 .as("Repetitions after the kill share a single replacement connection - one"
109 + " re-acquisition, not a fresh connection per method.")
110 .containsOnly(seen.get(2));
111 assertThat(seen.get(0))
112 .as("The replacement must be a genuinely different physical connection, not"
113 + " the dead one handed back again.")
114 .isNotSameAs(seen.get(2));
115
116 final IDatabaseConnection verifyConnection = environment.getConnection();
117 assertThat(rowCount(verifyConnection, TEST_TABLE))
118 .as("Each repetition's @DbUnitTearDown(DELETE_ALL) must have committed:"
119 + " a separate connection must see TEST_TABLE empty after the run,"
120 + " not the rows a never-committed teardown would leave behind"
121 + " (#965).")
122 .isZero();
123 } finally
124 {
125 deleteAllRowsQuietly(environment);
126 provider.close();
127 environment.closeConnection();
128 }
129 }
130
131 private static int rowCount(final IDatabaseConnection connection, final String tableName)
132 throws Exception
133 {
134 try (Statement statement = connection.getConnection().createStatement();
135 ResultSet resultSet =
136 statement.executeQuery("SELECT COUNT(*) FROM " + tableName))
137 {
138 resultSet.next();
139 return resultSet.getInt(1);
140 }
141 }
142
143 private static void deleteAllRowsQuietly(final DatabaseEnvironment environment)
144 {
145 try (Statement statement =
146 environment.getConnection().getConnection().createStatement())
147 {
148 statement.execute("DELETE FROM " + TEST_TABLE);
149 } catch (final Exception e)
150 {
151
152 }
153 }
154
155 @ExtendWith(DbUnitExtension.class)
156 @ClearRowCountCheckSystemProperties
157 @DbUnitConfig(closeConnectionAfterTest = false,
158 databaseTesterFactory = ReusedInstanceSample.SharedProviderTesterFactory.class)
159 @DbUnitRowCountCheck
160 static class ReusedInstanceSample
161 {
162 static DatabaseProfile profile;
163 static CachingConnectionProvider provider;
164 static final List<Connection> rawConnectionsSeen = new ArrayList<>();
165 static int repetitionsCompleted;
166
167
168
169
170
171 @DbUnitTestCase
172 static final PrepAndExpectedTestCase testCase = new DefaultPrepAndExpectedTestCase();
173
174 static void reset()
175 {
176 rawConnectionsSeen.clear();
177 repetitionsCompleted = 0;
178 }
179
180 @RepeatedTest(5)
181 @DbUnitPrep("annotation-it-prep.xml")
182 @DbUnitExpected(value = "annotation-it-expected.xml",
183 verify = @DbUnitVerifyTable(value = TEST_TABLE,
184 include = {"COLUMN0", "COLUMN1"}))
185 @DbUnitTearDown(operation = DbUnitOperation.DELETE_ALL)
186 void mutateTheSeededRow() throws Exception
187 {
188 final IDatabaseConnection connection = reusableConnection();
189 rawConnectionsSeen.add(connection.getConnection());
190 try (Statement statement = connection.getConnection().createStatement())
191 {
192 statement.execute("UPDATE " + TEST_TABLE
193 + " SET COLUMN1 = 'after' WHERE COLUMN0 = 'row0'");
194 }
195 }
196
197 @AfterEach
198 void dropTheCachedConnectionBetweenTheSecondAndThirdRepetition() throws Exception
199 {
200 repetitionsCompleted++;
201 if (repetitionsCompleted == 2)
202 {
203
204
205
206
207 reusableConnection().getConnection().close();
208 }
209 }
210
211 private static IDatabaseConnection reusableConnection() throws Exception
212 {
213 return ((DefaultPrepAndExpectedTestCase) testCase).getReusableConnection();
214 }
215
216 static class SharedProviderTesterFactory implements DatabaseTesterFactory
217 {
218 @Override
219 public IDatabaseTester getDatabaseTester() throws Exception
220 {
221 return new JdbcDatabaseTester(profile.getDriverClass(),
222 profile.getConnectionUrl(), profile.getUser(), profile.getPassword(),
223 profile.getSchema(), provider);
224 }
225 }
226 }
227 }