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.Statement;
27
28 import org.dbunit.DatabaseEnvironment;
29 import org.dbunit.DefaultDatabaseTester;
30 import org.dbunit.IDatabaseTester;
31 import org.dbunit.IOperationListener;
32 import org.dbunit.annotation.DbUnitRowCountCheck;
33 import org.dbunit.annotation.DbUnitSetup;
34 import org.dbunit.annotation.DbUnitTester;
35 import org.dbunit.database.DatabaseConfig;
36 import org.dbunit.database.IDatabaseConnection;
37 import org.dbunit.database.rowcount.ClearRowCountCheckSystemProperties;
38 import org.dbunit.database.rowcount.UnexpectedRowCountException;
39 import org.dbunit.operation.DbUnitOperation;
40 import org.junit.jupiter.api.Test;
41 import org.junit.jupiter.api.extension.ExtendWith;
42 import org.junit.platform.engine.TestExecutionResult;
43 import org.junit.platform.testkit.engine.EngineTestKit;
44 import org.junit.platform.testkit.engine.Event;
45
46
47
48
49
50
51
52
53 @ClearRowCountCheckSystemProperties
54 class DbUnitExtensionRowCountCheckIT
55 {
56 private static final String EMPTY_TABLE = "EMPTY_TABLE";
57
58 @Test
59 void testAfterTestExecution_rowLeakedIntoUnlistedTable_failsNamingIt() throws Exception
60 {
61 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
62 try
63 {
64 final IDatabaseConnection connection = connectionWithFeatureDisabled(environment);
65 LeakedRowSample.databaseTester = new DefaultDatabaseTester(connection);
66 LeakedRowSample.databaseTester
67 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
68
69 final Event failedEvent = EngineTestKit.engine("junit-jupiter")
70 .selectors(selectClass(LeakedRowSample.class)).execute().testEvents()
71 .failed().stream().findFirst().orElseThrow(
72 () -> new AssertionError("Expected one failed test event."));
73
74 final Throwable reported = failedEvent
75 .getRequiredPayload(TestExecutionResult.class).getThrowable()
76 .orElseThrow(() -> new AssertionError("Expected a reported throwable."));
77 assertThat(reported)
78 .as("A row left behind in a table absent from teardown must fail the"
79 + " test with UnexpectedRowCountException.")
80 .isInstanceOf(UnexpectedRowCountException.class);
81 assertThat(((UnexpectedRowCountException) reported).getDifferences())
82 .as("The failure must name the table the row leaked into; table name"
83 + " casing is database-dependent (PostgreSQL folds it to"
84 + " lowercase), so match case-insensitively.")
85 .anyMatch(difference -> difference.getTableName()
86 .equalsIgnoreCase(EMPTY_TABLE));
87 } finally
88 {
89 deleteAllRowsQuietly(environment, EMPTY_TABLE);
90 environment.closeConnection();
91 }
92 }
93
94 @Test
95 void testAfterTestExecution_excludedTableLeaksRows_passes() throws Exception
96 {
97 final DatabaseEnvironment environment = DatabaseEnvironment.getInstance();
98 try
99 {
100 final IDatabaseConnection connection = connectionWithFeatureDisabled(environment);
101 ExcludedTableSample.databaseTester = new DefaultDatabaseTester(connection);
102 ExcludedTableSample.databaseTester
103 .setOperationListener(IOperationListener.NO_OP_OPERATION_LISTENER);
104
105 EngineTestKit.engine("junit-jupiter")
106 .selectors(selectClass(ExcludedTableSample.class)).execute().testEvents()
107 .assertStatistics(stats -> stats.started(1).succeeded(1));
108 } finally
109 {
110 deleteAllRowsQuietly(environment, EMPTY_TABLE);
111 environment.closeConnection();
112 }
113 }
114
115 private static IDatabaseConnection connectionWithFeatureDisabled(
116 final DatabaseEnvironment environment) throws Exception
117 {
118 final IDatabaseConnection connection = environment.getConnection();
119 connection.getConfig().setFeature(DatabaseConfig.FEATURE_ROW_COUNT_CHECK, false);
120 return connection;
121 }
122
123 private static void deleteAllRowsQuietly(final DatabaseEnvironment environment,
124 final String tableName)
125 {
126 try (Statement statement =
127 environment.getConnection().getConnection().createStatement())
128 {
129 statement.execute("DELETE FROM " + tableName);
130 } catch (final Exception e)
131 {
132
133 }
134 }
135
136 @ExtendWith(DbUnitExtension.class)
137 @DbUnitRowCountCheck
138 @DbUnitSetup(operation = DbUnitOperation.NONE)
139 static class LeakedRowSample
140 {
141 @DbUnitTester
142 static IDatabaseTester databaseTester;
143
144 @Test
145 void testInsertRow_intoUnlistedTable_leavesRowForRowCountCheckToDetect() throws Exception
146 {
147 try (Statement statement =
148 databaseTester.getConnection().getConnection().createStatement())
149 {
150 statement.execute(
151 "INSERT INTO " + EMPTY_TABLE + " (COLUMN0) VALUES ('leaked')");
152 }
153 }
154 }
155
156 @ExtendWith(DbUnitExtension.class)
157 @DbUnitRowCountCheck(exclude = EMPTY_TABLE)
158 @DbUnitSetup(operation = DbUnitOperation.NONE)
159 static class ExcludedTableSample
160 {
161 @DbUnitTester
162 static IDatabaseTester databaseTester;
163
164 @Test
165 void testInsertRow_intoExcludedTable_rowCountCheckIgnoresIt() throws Exception
166 {
167 try (Statement statement =
168 databaseTester.getConnection().getConnection().createStatement())
169 {
170 statement.execute(
171 "INSERT INTO " + EMPTY_TABLE + " (COLUMN0) VALUES ('leaked')");
172 }
173 }
174 }
175 }