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.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.DriverManager;
28  import java.sql.ResultSet;
29  import java.sql.Statement;
30  
31  import org.dbunit.DatabaseEnvironment;
32  import org.dbunit.DatabaseProfile;
33  import org.dbunit.DefaultDatabaseTester;
34  import org.dbunit.IDatabaseTester;
35  import org.dbunit.JdbcDatabaseTester;
36  import org.dbunit.database.DatabaseConnection;
37  import org.dbunit.database.IDatabaseConnection;
38  import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
39  import org.dbunit.operation.DatabaseOperation;
40  import org.junit.jupiter.api.BeforeEach;
41  import org.junit.jupiter.api.Test;
42  import org.junit.jupiter.api.extension.ExtendWith;
43  import org.junit.platform.testkit.engine.EngineTestKit;
44  
45  /**
46   * Real-database (hsqldb) integration test of the <em>classic</em> {@link DbUnitExtension}
47   * path: {@code @ExtendWith(DbUnitExtension.class)} on its own, one plain unannotated
48   * {@link IDatabaseTester} field, and a {@code @BeforeEach} method configuring the dataset and
49   * the teardown operation - no {@code org.dbunit.annotation} annotation anywhere. This is the
50   * 3.5.0 style, still supported after the annotation rewrite routed every path through
51   * {@code AnnotatedTestExecutor}; {@code DbUnitExtensionLifecycleTest}'s {@code CallLoggingTester}
52   * returns a {@code null} connection, so only a real database proves {@code onSetup()} seeds the
53   * {@code @BeforeEach} dataset and {@code onTearDown()} still runs the {@code @BeforeEach}-set
54   * operation rather than a reset {@code NONE}.
55   */
56  class DbUnitExtensionClassicPathIT
57  {
58      private static final String TEST_TABLE = "TEST_TABLE";
59  
60      @Test
61      void testClassicPath_beforeEachSetsDatasetAndTeardownOperation_seedsThenTearsDown()
62              throws Exception
63      {
64          final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
65          ClassicSample.profile = environment.getProfile();
66          try
67          {
68              EngineTestKit.engine("junit-jupiter")
69                      .selectors(selectClass(ClassicSample.class)).execute().testEvents()
70                      .assertStatistics(stats -> stats.started(1).succeeded(1));
71  
72              final IDatabaseConnection verifyConnection = environment.getConnection();
73              assertThat(rowCount(verifyConnection, TEST_TABLE))
74                      .as("The @BeforeEach method's databaseTester.setTearDownOperation(DELETE_ALL)"
75                              + " must survive to onTearDown() - the classic path must not reset the"
76                              + " tester's teardown operation to NONE when no @DbUnitTearDown is"
77                              + " declared.")
78                      .isZero();
79          } finally
80          {
81              deleteAllRowsQuietly(environment);
82              environment.closeConnection();
83          }
84      }
85  
86      @Test
87      void testClassicPath_fixedConnectionTester_onSetupRunsAgainstAnOpenConnection()
88              throws Exception
89      {
90          final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
91          final DatabaseProfile profile = environment.getProfile();
92          deleteAllRowsQuietly(environment);
93  
94          final Connection fixedConnection = DriverManager.getConnection(
95                  profile.getConnectionUrl(), profile.getUser(), profile.getPassword());
96          FixedConnectionSample.databaseConnection =
97                  new DatabaseConnection(fixedConnection, profile.getSchema());
98          try
99          {
100             EngineTestKit.engine("junit-jupiter")
101                     .selectors(selectClass(FixedConnectionSample.class)).execute().testEvents()
102                     .assertStatistics(stats -> stats.started(1).succeeded(1));
103 
104             final IDatabaseConnection verifyConnection = environment.getConnection();
105             assertThat(rowCount(verifyConnection, TEST_TABLE))
106                     .as("The classic path must not close a DefaultDatabaseTester's fixed"
107                             + " connection before onSetup(): onSetup()'s CLEAN_INSERT then runs"
108                             + " against a closed connection and seeds nothing.")
109                     .isEqualTo(1);
110         } finally
111         {
112             closeQuietly(fixedConnection);
113             deleteAllRowsQuietly(environment);
114             environment.closeConnection();
115         }
116     }
117 
118     private static void closeQuietly(final Connection connection)
119     {
120         try
121         {
122             if (!connection.isClosed())
123             {
124                 connection.close();
125             }
126         } catch (final Exception e)
127         {
128             // best-effort
129         }
130     }
131 
132     private static int rowCount(final IDatabaseConnection connection, final String tableName)
133             throws Exception
134     {
135         try (Statement statement = connection.getConnection().createStatement();
136                 ResultSet resultSet =
137                         statement.executeQuery("SELECT COUNT(*) FROM " + tableName))
138         {
139             resultSet.next();
140             return resultSet.getInt(1);
141         }
142     }
143 
144     private static void deleteAllRowsQuietly(final DatabaseEnvironment environment)
145     {
146         try (Statement statement =
147                 environment.getConnection().getConnection().createStatement())
148         {
149             statement.execute("DELETE FROM " + TEST_TABLE);
150         } catch (final Exception e)
151         {
152             // best-effort cleanup only; a failure here must not fail the test that already ran
153         }
154     }
155 
156     @ExtendWith(DbUnitExtension.class)
157     static class ClassicSample
158     {
159         static DatabaseProfile profile;
160 
161         final IDatabaseTester databaseTester;
162 
163         ClassicSample() throws Exception
164         {
165             databaseTester = new JdbcDatabaseTester(profile.getDriverClass(),
166                     profile.getConnectionUrl(), profile.getUser(), profile.getPassword(),
167                     profile.getSchema());
168         }
169 
170         @BeforeEach
171         void configureTester() throws Exception
172         {
173             databaseTester.setDataSet(new FlatXmlDataSetBuilder()
174                     .build(getClass().getResource("annotation-it-prep.xml")));
175             databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);
176         }
177 
178         @Test
179         void seededRowIsVisibleBeforeTheTestBody() throws Exception
180         {
181             try (Statement statement =
182                     databaseTester.getConnection().getConnection().createStatement();
183                     ResultSet resultSet = statement.executeQuery("SELECT COUNT(*) FROM "
184                             + TEST_TABLE + " WHERE COLUMN0 = 'row0'"))
185             {
186                 resultSet.next();
187                 assertThat(resultSet.getInt(1))
188                         .as("onSetup()'s default CLEAN_INSERT must seed the @BeforeEach dataset"
189                                 + " before the test body runs on the classic path.")
190                         .isEqualTo(1);
191             }
192         }
193     }
194 
195     // A DefaultDatabaseTester built from one fixed IDatabaseConnection: getConnection() returns
196     // that same object to the row count baseline probe and to onSetup(). The classic path must
197     // leave it open through onSetup(), not close it after resolving the (disabled) baseline.
198 
199     @ExtendWith(DbUnitExtension.class)
200     static class FixedConnectionSample
201     {
202         static IDatabaseConnection databaseConnection;
203 
204         final IDatabaseTester databaseTester;
205 
206         FixedConnectionSample()
207         {
208             databaseTester = new DefaultDatabaseTester(databaseConnection);
209         }
210 
211         @BeforeEach
212         void configureTester() throws Exception
213         {
214             databaseTester.setDataSet(new FlatXmlDataSetBuilder()
215                     .build(getClass().getResource("annotation-it-prep.xml")));
216             databaseTester.setTearDownOperation(DatabaseOperation.NONE);
217         }
218 
219         @Test
220         void seedsThroughOnSetup()
221         {
222             // The seeded row is asserted through a separate connection in the enclosing test -
223             // this fixed connection is closed by the enclosing test's cleanup.
224         }
225     }
226 }