1 /*
2 *
3 * The DbUnit Database Testing Framework
4 * Copyright (C)2002-2008, 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;
22
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.dbunit.database.IDatabaseConnection;
27 import org.dbunit.dataset.CompositeDataSet;
28 import org.dbunit.dataset.DataSetException;
29 import org.dbunit.dataset.IDataSet;
30 import org.dbunit.dataset.ITable;
31 import org.dbunit.dataset.SortedTable;
32 import org.dbunit.dataset.filter.DefaultColumnFilter;
33 import org.dbunit.util.fileloader.DataFileLoader;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38 * Test case base class supporting prep data and expected data. Prep data is the
39 * data needed for the test to run. Expected data is the data needed to compare
40 * if the test ran successfully.
41 *
42 * Use this class in two ways:
43 * <ol>
44 * <li>Dependency inject it as its interface into a test class.</li>
45 * <p>
46 * Configure a bean of its interface, injecting a IDatabaseTester and a
47 * DataFileLoader using the databaseTester and a dataFileLoader properties.
48 * </p>
49 *
50 * <li>Extend it in a test class.</li>
51 * <p>
52 * Obtain IDatabaseTester and DataFileLoader instances (possibly dependency
53 * injecting them into the test class) and set them accordingly, probably in a
54 * setup type of method, such as:
55 *
56 * <pre>
57 * @Before
58 * public void setDbunitTestDependencies() {
59 * setDatabaseTester(databaseTester);
60 * setDataFileLoader(dataFileLoader);
61 * }
62 * </pre>
63 *
64 * </p>
65 * </ol>
66 *
67 * To setup, execute, and clean up tests, call the configureTest(), preTest(),
68 * and postTest() methods. Note there is a preTest() convenience method that
69 * takes the same parameters as the configureTest() method; use it instead of
70 * using both configureTest() and preTest().
71 *
72 * Where the test case calls them depends on data needs:
73 * <ul>
74 * <li>For the whole test case, i.e. in setUp() and tearDown() or @Before
75 * and @After.</li>
76 * <li>In each test method.</li>
77 * <li>Or some combination of both test case setup/teardown and test methods.</li>
78 * </ul>
79 *
80 * <h4>When each test method requires different prep and expected data</h4>
81 *
82 * If each test method requires its own prep and expected data, then the test
83 * methods will look something like the following:
84 *
85 * <pre>
86 * @Autowired
87 * private PrepAndExpectedTestCase tc;
88 *
89 * @Test
90 * public void testExample() throws Exception {
91 * String[] prepDataFiles = {}; // define prep files
92 * String[] expectedDataFiles = {}; // define expected files
93 * VerifyTableDefinition[] tables = {}; // define tables to verify
94 *
95 * tc.preTest(tables, prepDataFiles, expectedDataFiles);
96 *
97 * // execute test
98 *
99 * tc.postTest();
100 * }
101 * </pre>
102 *
103 * <h4>When all test methods share the same prep and/or expected data</h4>
104 *
105 * If each test method can share all of the prep and/or expected data, then use
106 * setUp() for the configureTest() and preTest() calls and tearDown() for the
107 * postTest() call. The methods will look something like the following:
108 *
109 * <pre>
110 * @Override
111 * protected void setUp() throws Exception {
112 * setDatabaseTester(databaseTester);
113 * setDataFileLoader(dataFileLoader);
114 *
115 * String[] prepDataFiles = {}; //define prep files
116 * String[] expectedDataFiles = {}; // define expected files
117 * VerifyTableDefinition[] tables = {}; //define tables to verify
118 *
119 * preTest(tables, prepDataFiles, expectedDataFiles);
120 *
121 * // call this if overriding setUp() and databaseTester & dataFileLoader are already set.
122 * super.setUp();
123 * }
124 *
125 * @Override
126 * protected void tearDown() throws Exception {
127 * postTest();
128 * super.tearDown();
129 * }
130 *
131 * @Test
132 * public void testExample() throws Exception {
133 * // execute test
134 * }
135 * </pre>
136 *
137 * Note that it is unlikely that all test methods can share the same expected
138 * data.
139 *
140 * <h4>Sharing common (but not all) prep or expected data among test methods.</h4>
141 *
142 * Put common data in one or more files and pass the needed ones in the correct
143 * data file array.
144 *
145 * <h4>Notes</h4>
146 * <ol>
147 * <li>For additional examples, refer to the ITs (listed in the See Also
148 * section).</li>
149 * <li>To change the setup or teardown operation (e.g. change the teardown to
150 * org.dbunit.operation.DatabaseOperation.DELETE_ALL), set the setUpOperation or
151 * tearDownOperation property on the databaseTester.</li>
152 * <li>To set DatabaseConfig features/properties, one way is to extend this
153 * class and override the setUpDatabaseConfig(DatabaseConfig config) method from
154 * DatabaseTestCase.</li>
155 * </ol>
156 *
157 * @see org.dbunit.DefaultPrepAndExpectedTestCaseDiIT
158 * @see org.dbunit.DefaultPrepAndExpectedTestCaseExtIT
159 *
160 * @author Jeff Jensen jeffjensen AT users.sourceforge.net
161 * @author Last changed by: $Author$
162 * @version $Revision$ $Date$
163 * @since 2.4.8
164 */
165 public class DefaultPrepAndExpectedTestCase extends DBTestCase implements
166 PrepAndExpectedTestCase {
167 private final Logger LOG =
168 LoggerFactory.getLogger(DefaultPrepAndExpectedTestCase.class);
169
170 private IDatabaseTester databaseTester;
171 private DataFileLoader dataFileLoader;
172
173 private IDataSet prepDs;
174 private IDataSet expectedDs;
175 private VerifyTableDefinition[] tableDefs;
176
177 /** Create new instance. */
178 public DefaultPrepAndExpectedTestCase() {
179 }
180
181 /**
182 * Create new instance with specified dataFileLoader and databasetester.
183 *
184 * @param dataFileLoader
185 * Load to use for loading the data files.
186 * @param databaseTester
187 * Tester to use for database manipulation.
188 */
189 public DefaultPrepAndExpectedTestCase(DataFileLoader dataFileLoader,
190 IDatabaseTester databaseTester) {
191 this.dataFileLoader = dataFileLoader;
192 this.databaseTester = databaseTester;
193 }
194
195 /**
196 * Create new instance with specified test case name.
197 *
198 * @param name
199 * The test case name.
200 */
201 public DefaultPrepAndExpectedTestCase(String name) {
202 super(name);
203 }
204
205 /**
206 * {@inheritDoc} This implementation returns the databaseTester set by the
207 * test.
208 */
209 public IDatabaseTester newDatabaseTester() throws Exception {
210 // questionable, but there is not a "setter" for any parent...
211 return databaseTester;
212 }
213
214 /**
215 * {@inheritDoc} Returns the prep dataset.
216 */
217 public IDataSet getDataSet() throws Exception {
218 return prepDs;
219 }
220
221 /**
222 * {@inheritDoc}
223 */
224 public void configureTest(VerifyTableDefinition[] tables,
225 String[] prepDataFiles, String[] expectedDataFiles)
226 throws Exception {
227 LOG.debug("configureTest: saving instance variables");
228 this.prepDs = makeCompositeDataSet(prepDataFiles);
229 this.expectedDs = makeCompositeDataSet(expectedDataFiles);
230 this.tableDefs = tables;
231 }
232
233 /**
234 * {@inheritDoc}
235 */
236 public void preTest() throws Exception {
237 setupData();
238 }
239
240 /**
241 * {@inheritDoc}
242 */
243 public void preTest(VerifyTableDefinition[] tables, String[] prepDataFiles,
244 String[] expectedDataFiles) throws Exception {
245 configureTest(tables, prepDataFiles, expectedDataFiles);
246 preTest();
247 }
248
249 /**
250 * {@inheritDoc}
251 */
252 public void postTest() throws Exception {
253 postTest(true);
254 }
255
256 /**
257 * {@inheritDoc}
258 */
259 public void postTest(boolean verifyData) throws Exception {
260 try {
261 if (verifyData) {
262 verifyData();
263 }
264 } finally {
265 // it is deliberate to have cleanup exceptions shadow verify
266 // failures so user knows db is probably in unknown state (for
267 // those not using an in-memory db or transaction rollback),
268 // otherwise would mask probable cause of subsequent test failures
269 cleanupData();
270 }
271 }
272
273 /**
274 * {@inheritDoc}
275 */
276 public void cleanupData() throws Exception {
277 IDataSet dataset = new CompositeDataSet(prepDs, expectedDs);
278 String tableNames[] = dataset.getTableNames();
279 int count = tableNames.length;
280 LOG.info("cleanupData: about to clean up {} tables={}", new Integer(
281 count), tableNames);
282
283 if (databaseTester == null) {
284 throw new IllegalStateException(
285 "databaseTester is null; must configure or set it first");
286 }
287
288 databaseTester.setDataSet(dataset);
289 databaseTester.onTearDown();
290 LOG.debug("verifyData: Clean up done");
291 }
292
293 /**
294 * Use the provided databaseTester to prep the database with the provided
295 * prep dataset. See {@link org.dbunit.IDatabaseTester#onSetup()}.
296 *
297 * @throws Exception
298 */
299 public void setupData() throws Exception {
300 LOG.debug("setupData: setting prep dataset and inserting rows");
301 if (databaseTester == null) {
302 throw new IllegalStateException(
303 "databaseTester is null; must configure or set it first");
304 }
305
306 databaseTester.setDataSet(prepDs);
307 databaseTester.onSetup();
308 }
309
310 /**
311 * For the provided VerifyTableDefinitions, verify each table's actual
312 * results are as expected. Uses the connection from the provided
313 * databaseTester.
314 *
315 * @throws Exception
316 */
317 public void verifyData() throws Exception {
318 if (databaseTester == null) {
319 throw new IllegalStateException(
320 "databaseTester is null; must configure or set it first");
321 }
322
323 IDatabaseConnection connection = databaseTester.getConnection();
324
325 try {
326 int count = tableDefs.length;
327 LOG.info("verifyData: about to verify {} tables={}", new Integer(
328 count), tableDefs);
329 if (count == 0) {
330 LOG.warn("No tables to verify;"
331 + " no VerifyTableDefinitions specified");
332 }
333
334 for (int i = 0; i < count; i++) {
335 VerifyTableDefinition td = tableDefs[i];
336 String[] excludeColumns = td.getColumnExclusionFilters();
337 String[] includeColumns = td.getColumnInclusionFilters();
338 String tableName = td.getTableName();
339
340 LOG.info("Verifying table '{}'", tableName);
341
342 LOG.debug(" Loading its rows from expected dataset");
343 ITable expectedTable = null;
344 try {
345 expectedTable = expectedDs.getTable(tableName);
346 } catch (DataSetException e) {
347 final String msg =
348 "Problem obtaining table '" + tableName
349 + "' from expected dataset";
350 LOG.error(msg, e);
351 throw new DataSetException(msg, e);
352 }
353
354 LOG.debug(" Loading its rows from actual table");
355 ITable actualTable = null;
356 try {
357 actualTable = connection.createTable(tableName);
358 } catch (DataSetException e) {
359 final String msg =
360 "Problem obtaining table '" + tableName
361 + "' from actual dataset";
362 LOG.error(msg, e);
363 throw new DataSetException(msg, e);
364 }
365
366 verifyData(expectedTable, actualTable, excludeColumns,
367 includeColumns);
368 }
369 } finally {
370 LOG.debug("verifyData: Verification done, closing connection");
371 connection.close();
372 }
373 }
374
375 /**
376 * For the specified expected and actual tables (and excluding and including
377 * the specified columns), verify the actual data is as expected.
378 *
379 * @param expectedTable
380 * The expected table to compare the actual table to.
381 * @param actualTable
382 * The actual table to compare to the expected table.
383 * @param excludeColumns
384 * The column names to exclude from comparison. See
385 * {@link org.dbunit.dataset.filter.DefaultColumnFilter#excludeColumn(String)}
386 * .
387 * @param includeColumns
388 * The column names to only include in comparison. See
389 * {@link org.dbunit.dataset.filter.DefaultColumnFilter#includeColumn(String)}
390 * .
391 * @throws DatabaseUnitException
392 */
393 public void verifyData(ITable expectedTable, ITable actualTable,
394 String[] excludeColumns, String[] includeColumns)
395 throws DatabaseUnitException {
396 // Filter out the columns from the expected and actual results
397 LOG.debug("Applying filters to expected table");
398 ITable expectedFilteredTable =
399 applyColumnFilters(expectedTable, excludeColumns,
400 includeColumns);
401 LOG.debug("Applying filters to actual table");
402 ITable actualFilteredTable =
403 applyColumnFilters(actualTable, excludeColumns, includeColumns);
404
405 LOG.debug("Sorting expected table");
406 SortedTable expectedSortedTable =
407 new SortedTable(expectedFilteredTable);
408 LOG.debug("Sorting actual table");
409 SortedTable actualSortedTable =
410 new SortedTable(actualFilteredTable, expectedFilteredTable
411 .getTableMetaData());
412
413 LOG.debug("Comparing expected table to actual table");
414 Assertion.assertEquals(expectedSortedTable, actualSortedTable);
415 }
416
417 /**
418 * Make a <code>IDataSet</code> from the specified files.
419 *
420 * @param dataFiles
421 * Represents the array of dbUnit data files.
422 * @return The composite dataset.
423 * @throws DataSetException
424 * On dbUnit errors.
425 */
426 public IDataSet makeCompositeDataSet(String[] dataFiles)
427 throws DataSetException {
428 if (dataFileLoader == null) {
429 throw new IllegalStateException(
430 "dataFileLoader is null; must configure or set it first");
431 }
432
433 int count = dataFiles.length;
434 LOG.debug("makeCompositeDataSet: dataFiles count=" + count);
435 if (count == 0) {
436 LOG.info("makeCompositeDataSet: Specified zero data files");
437 }
438
439 List list = new ArrayList();
440 for (int i = 0; i < count; i++) {
441 IDataSet ds = dataFileLoader.load(dataFiles[i]);
442 list.add(ds);
443 }
444
445 IDataSet[] dataSet = (IDataSet[]) list.toArray(new IDataSet[] {});
446 IDataSet compositeDS = new CompositeDataSet(dataSet);
447 return compositeDS;
448 }
449
450 /**
451 * Apply the specified exclude and include column filters to the specified
452 * table.
453 *
454 * @param table
455 * The table to apply the filters to.
456 * @param excludeColumns
457 * The exclude filters; use null or empty array to mean exclude
458 * none.
459 * @param includeColumns
460 * The include filters; use null to mean include all.
461 * @return The filtered table.
462 * @throws DataSetException
463 */
464 public ITable applyColumnFilters(ITable table, String[] excludeColumns,
465 String[] includeColumns) throws DataSetException {
466 ITable filteredTable = table;
467
468 if (table == null) {
469 throw new IllegalArgumentException("table is null");
470 }
471
472 // note: dbunit interprets an empty inclusion filter array as one
473 // not wanting to compare anything!
474 if (includeColumns == null) {
475 LOG.debug("applyColumnFilters: including columns=(all)");
476 } else {
477 LOG.debug("applyColumnFilters: including columns='{}'",
478 new Object[] {includeColumns});
479 filteredTable =
480 DefaultColumnFilter.includedColumnsTable(filteredTable,
481 includeColumns);
482 }
483
484 if (excludeColumns == null || excludeColumns.length == 0) {
485 LOG.debug("applyColumnFilters: excluding columns=(none)");
486 } else {
487 LOG.debug("applyColumnFilters: excluding columns='{}'",
488 new Object[] {excludeColumns});
489 filteredTable =
490 DefaultColumnFilter.excludedColumnsTable(filteredTable,
491 excludeColumns);
492 }
493
494 return filteredTable;
495 }
496
497 /**
498 * {@inheritDoc}
499 */
500 public IDataSet getPrepDataset() {
501 return prepDs;
502 }
503
504 /**
505 * {@inheritDoc}
506 */
507 public IDataSet getExpectedDataset() {
508 return expectedDs;
509 }
510
511 /**
512 * Get the databaseTester.
513 *
514 * @see {@link databaseTester}.
515 *
516 * @return The databaseTester.
517 */
518 public IDatabaseTester getDatabaseTester() {
519 return databaseTester;
520 }
521
522 /**
523 * Set the databaseTester.
524 *
525 * @see {@link databaseTester}.
526 *
527 * @param databaseTester
528 * The databaseTester to set.
529 */
530 public void setDatabaseTester(IDatabaseTester databaseTester) {
531 this.databaseTester = databaseTester;
532 }
533
534 /**
535 * Get the dataFileLoader.
536 *
537 * @see {@link dataFileLoader}.
538 *
539 * @return The dataFileLoader.
540 */
541 public DataFileLoader getDataFileLoader() {
542 return dataFileLoader;
543 }
544
545 /**
546 * Set the dataFileLoader.
547 *
548 * @see {@link dataFileLoader}.
549 *
550 * @param dataFileLoader
551 * The dataFileLoader to set.
552 */
553 public void setDataFileLoader(DataFileLoader dataFileLoader) {
554 this.dataFileLoader = dataFileLoader;
555 }
556
557 /**
558 * Set the prepDs.
559 *
560 * @see {@link prepDs}.
561 *
562 * @param prepDs
563 * The prepDs to set.
564 */
565 public void setPrepDs(IDataSet prepDs) {
566 this.prepDs = prepDs;
567 }
568
569 /**
570 * Set the expectedDs.
571 *
572 * @see {@link expectedDs}.
573 *
574 * @param expectedDs
575 * The expectedDs to set.
576 */
577 public void setExpectedDs(IDataSet expectedDs) {
578 this.expectedDs = expectedDs;
579 }
580
581 /**
582 * Get the tableDefs.
583 *
584 * @see {@link tableDefs}.
585 *
586 * @return The tableDefs.
587 */
588 public VerifyTableDefinition[] getTableDefs() {
589 return tableDefs;
590 }
591
592 /**
593 * Set the tableDefs.
594 *
595 * @see {@link tableDefs}.
596 *
597 * @param tableDefs
598 * The tableDefs to set.
599 */
600 public void setTableDefs(VerifyTableDefinition[] tableDefs) {
601 this.tableDefs = tableDefs;
602 }
603 }