View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2026, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  package org.dbunit.database;
22  
23  import static org.assertj.core.api.Assertions.assertThat;
24  
25  import java.sql.Connection;
26  import java.sql.DriverManager;
27  
28  import org.dbunit.DatabaseEnvironment;
29  import org.dbunit.DatabaseProfile;
30  import org.dbunit.dataset.IDataSet;
31  import org.junit.jupiter.api.AfterEach;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Integration tests for {@link CachingConnectionProvider} against whichever real database is
37   * configured by the active Maven profile (see {@link DatabaseEnvironment}), proving the liveness
38   * detection and reconnection logic against a real JDBC driver, not just the H2 in-memory driver
39   * used by {@link CachingConnectionProviderTest}.
40   *
41   * <p>
42   * Builds its own independent JDBC connections directly from the {@link DatabaseProfile} rather
43   * than going through {@link DatabaseEnvironment#getConnection()}'s shared singleton, so it cannot
44   * interfere with the connection lifecycle other IT classes depend on.
45   *
46   * @since 3.4.0
47   */
48  class CachingConnectionProviderIT
49  {
50      private DatabaseProfile profile;
51  
52      private CachingConnectionProvider provider;
53  
54      @BeforeEach
55      void setUp() throws Exception
56      {
57          // assign provider first so tearDown()'s provider.close() is always
58          // safe to call, even if the profile lookup or driver loading below
59          // fails partway through setUp()
60          provider = new CachingConnectionProvider();
61          profile = DatabaseEnvironment.getInstance().getProfile();
62          Class.forName(profile.getDriverClass());
63      }
64  
65      @AfterEach
66      void tearDown() throws Exception
67      {
68          provider.close();
69      }
70  
71      @Test
72      void testGetConnection_calledTwice_returnsSameConnectionAndPreservesMetadataCache()
73              throws Exception
74      {
75          final IDatabaseConnection first = provider.getConnection(this::createConnection);
76          final IDataSet firstDataSet = first.createDataSet();
77  
78          final IDatabaseConnection second = provider.getConnection(this::createConnection);
79          final IDataSet secondDataSet = second.createDataSet();
80  
81          assertThat(second).as("A second call while the cached connection is alive must reuse it.")
82                  .isSameAs(first);
83          assertThat(secondDataSet)
84                  .as("Reusing the connection must also reuse its accumulated table-metadata "
85                          + "cache (DatabaseDataSet), not re-fetch it - that avoided re-fetch is the "
86                          + "entire point of this feature.")
87                  .isSameAs(firstDataSet);
88      }
89  
90      @Test
91      void testGetConnection_afterUnderlyingConnectionDies_transparentlyReconnectsAndStaysUsable()
92              throws Exception
93      {
94          final IDatabaseConnection first = provider.getConnection(this::createConnection);
95          // Simulate a DB blip / idle-timeout kill from the client's point of view.
96          first.getConnection().close();
97  
98          final IDatabaseConnection second = provider.getConnection(this::createConnection);
99  
100         assertThat(second).as("A dead cached connection must be replaced, not returned as-is.")
101                 .isNotSameAs(first);
102         assertThat(second.getRowCount("TEST_TABLE"))
103                 .as("The replacement connection must be a real, working connection against the "
104                         + "configured database.")
105                 .isGreaterThanOrEqualTo(0);
106     }
107 
108     @Test
109     void testClose_thenGetConnection_reconnectsSuccessfully() throws Exception
110     {
111         final IDatabaseConnection first = provider.getConnection(this::createConnection);
112 
113         provider.close();
114 
115         assertThat(first.getConnection().isClosed())
116                 .as("close() must close the connection it hands back control of.").isTrue();
117 
118         final IDatabaseConnection second = provider.getConnection(this::createConnection);
119 
120         assertThat(second)
121                 .as("Closing the provider must create a new connection.")
122                 .isNotSameAs(first);
123         assertThat(second.getRowCount("TEST_TABLE"))
124                 .as("The connection created after close() must be usable.")
125                 .isGreaterThanOrEqualTo(0);
126     }
127 
128     private IDatabaseConnection createConnection() throws Exception
129     {
130         final Connection jdbcConnection = DriverManager.getConnection(profile.getConnectionUrl(),
131                 profile.getUser(), profile.getPassword());
132         return new DatabaseConnection(jdbcConnection, profile.getSchema());
133     }
134 }