All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.springtestdbunit.DbUnitRunner Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
/*
 * Copyright 2002-2016 the original author or authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.springtestdbunit;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.CompositeDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.filter.IColumnFilter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.github.springtestdbunit.annotation.DatabaseOperation;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.github.springtestdbunit.annotation.DatabaseSetups;
import com.github.springtestdbunit.annotation.DatabaseTearDown;
import com.github.springtestdbunit.annotation.DatabaseTearDowns;
import com.github.springtestdbunit.annotation.ExpectedDatabase;
import com.github.springtestdbunit.annotation.ExpectedDatabases;
import com.github.springtestdbunit.assertion.DatabaseAssertion;
import com.github.springtestdbunit.dataset.DataSetLoader;
import com.github.springtestdbunit.dataset.DataSetModifier;

/**
 * Internal delegate class used to run tests with support for {@link DatabaseSetup @DatabaseSetup},
 * {@link DatabaseTearDown @DatabaseTearDown} and {@link ExpectedDatabase @ExpectedDatabase} annotations.
 *
 * @author Phillip Webb
 * @author Mario Zagar
 * @author Sunitha Rajarathnam
 * @author Oleksii Lomako
 */
public class DbUnitRunner {

	private static final Log logger = LogFactory.getLog(DbUnitTestExecutionListener.class);

	/**
	 * Called before a test method is executed to perform any database setup.
	 * @param testContext The test context
	 * @throws Exception
	 */
	public void beforeTestMethod(DbUnitTestContext testContext) throws Exception {
		Annotations annotations = Annotations.get(testContext, DatabaseSetups.class,
				DatabaseSetup.class);
		setupOrTeardown(testContext, true, AnnotationAttributes.get(annotations));
	}

	/**
	 * Called after a test method is executed to perform any database teardown and to check expected results.
	 * @param testContext The test context
	 * @throws Exception
	 */
	public void afterTestMethod(DbUnitTestContext testContext) throws Exception {
		try {
			try {
				verifyExpected(testContext,
						Annotations.get(testContext, ExpectedDatabases.class, ExpectedDatabase.class));
			} finally {
				Annotations annotations = Annotations.get(testContext, DatabaseTearDowns.class,
						DatabaseTearDown.class);
				try {
					setupOrTeardown(testContext, false, AnnotationAttributes.get(annotations));
				} catch (RuntimeException ex) {
					if (testContext.getTestException() == null) {
						throw ex;
					}
					if (logger.isWarnEnabled()) {
						logger.warn("Unable to throw database cleanup exception due to existing test error", ex);
					}
				}
			}
		} finally {
			testContext.getConnections().closeAll();
		}
	}

	private void verifyExpected(DbUnitTestContext testContext, Annotations annotations)
			throws Exception {
		if (testContext.getTestException() != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Skipping @DatabaseTest expectation due to test exception "
						+ testContext.getTestException().getClass());
			}
			return;
		}
		DatabaseConnections connections = testContext.getConnections();
		DataSetModifier modifier = getModifier(testContext, annotations);
		boolean override = false;
		for (ExpectedDatabase annotation : annotations.getMethodAnnotations()) {
			verifyExpected(testContext, connections, modifier, annotation);
			override |= annotation.override();
		}
		if (!override) {
			for (ExpectedDatabase annotation : annotations.getClassAnnotations()) {
				verifyExpected(testContext, connections, modifier, annotation);
			}
		}
	}

	private void verifyExpected(DbUnitTestContext testContext, DatabaseConnections connections,
			DataSetModifier modifier, ExpectedDatabase annotation)
					throws Exception, DataSetException, SQLException, DatabaseUnitException {
		String query = annotation.query();
		String table = annotation.table();
		IDataSet expectedDataSet = loadDataset(testContext, annotation.value(), modifier);
		IDatabaseConnection connection = connections.get(annotation.connection());
		if (expectedDataSet != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Veriftying @DatabaseTest expectation using " + annotation.value());
			}
			DatabaseAssertion assertion = annotation.assertionMode().getDatabaseAssertion();
			List columnFilters = getColumnFilters(annotation);
			if (StringUtils.hasLength(query)) {
				Assert.hasLength(table, "The table name must be specified when using a SQL query");
				ITable expectedTable = expectedDataSet.getTable(table);
				ITable actualTable = connection.createQueryTable(table, query);
				assertion.assertEquals(expectedTable, actualTable, columnFilters);
			} else if (StringUtils.hasLength(table)) {
				ITable actualTable = connection.createTable(table);
				ITable expectedTable = expectedDataSet.getTable(table);
				assertion.assertEquals(expectedTable, actualTable, columnFilters);
			} else {
				IDataSet actualDataSet = connection.createDataSet();
				assertion.assertEquals(expectedDataSet, actualDataSet, columnFilters);
			}
		}
	}

	private DataSetModifier getModifier(DbUnitTestContext testContext, Annotations annotations) {
		DataSetModifiers modifiers = new DataSetModifiers();
		for (ExpectedDatabase annotation : annotations) {
			for (Class modifierClass : annotation.modifiers()) {
				modifiers.add(testContext.getTestInstance(), modifierClass);
			}
		}
		return modifiers;
	}

	private void setupOrTeardown(DbUnitTestContext testContext, boolean isSetup,
			Collection annotations) throws Exception {
		DatabaseConnections connections = testContext.getConnections();
		for (AnnotationAttributes annotation : annotations) {
			List datasets = loadDataSets(testContext, annotation);
			DatabaseOperation operation = annotation.getType();
			org.dbunit.operation.DatabaseOperation dbUnitOperation = getDbUnitDatabaseOperation(testContext, operation);
			if (!datasets.isEmpty()) {
				if (logger.isDebugEnabled()) {
					logger.debug("Executing " + (isSetup ? "Setup" : "Teardown") + " of @DatabaseTest using "
							+ operation + " on " + datasets.toString());
				}
				IDatabaseConnection connection = connections.get(annotation.getConnection());
				IDataSet dataSet = new CompositeDataSet(datasets.toArray(new IDataSet[datasets.size()]));
				dbUnitOperation.execute(connection, dataSet);
			}
		}
	}

	private List loadDataSets(DbUnitTestContext testContext, AnnotationAttributes annotation)
			throws Exception {
		List datasets = new ArrayList();
		for (String dataSetLocation : annotation.getValue()) {
			datasets.add(loadDataset(testContext, dataSetLocation, DataSetModifier.NONE));
		}
		if (datasets.isEmpty()) {
			datasets.add(getFullDatabaseDataSet(testContext, annotation.getConnection()));
		}
		return datasets;
	}

	private IDataSet getFullDatabaseDataSet(DbUnitTestContext testContext, String name) throws Exception {
		IDatabaseConnection connection = testContext.getConnections().get(name);
		return connection.createDataSet();
	}

	private IDataSet loadDataset(DbUnitTestContext testContext, String dataSetLocation, DataSetModifier modifier)
			throws Exception {
		DataSetLoader dataSetLoader = testContext.getDataSetLoader();
		if (StringUtils.hasLength(dataSetLocation)) {
			IDataSet dataSet = dataSetLoader.loadDataSet(testContext.getTestClass(), dataSetLocation);
			dataSet = modifier.modify(dataSet);
			Assert.notNull(dataSet,
					"Unable to load dataset from \"" + dataSetLocation + "\" using " + dataSetLoader.getClass());
			return dataSet;
		}
		return null;
	}

	private List getColumnFilters(ExpectedDatabase annotation) throws Exception {
		Class[] columnFilterClasses = annotation.columnFilters();
		List columnFilters = new LinkedList();
		for (Class columnFilterClass : columnFilterClasses) {
			columnFilters.add(columnFilterClass.newInstance());
		}
		return columnFilters;
	}

	private org.dbunit.operation.DatabaseOperation getDbUnitDatabaseOperation(DbUnitTestContext testContext,
			DatabaseOperation operation) {
		org.dbunit.operation.DatabaseOperation databaseOperation = testContext.getDatbaseOperationLookup()
				.get(operation);
		Assert.state(databaseOperation != null, "The database operation " + operation + " is not supported");
		return databaseOperation;
	}

	private static class AnnotationAttributes {

		private final DatabaseOperation type;

		private final String[] value;

		private final String connection;

		public AnnotationAttributes(Annotation annotation) {
			Assert.state((annotation instanceof DatabaseSetup) || (annotation instanceof DatabaseTearDown),
					"Only DatabaseSetup and DatabaseTearDown annotations are supported");
			Map attributes = AnnotationUtils.getAnnotationAttributes(annotation);
			this.type = (DatabaseOperation) attributes.get("type");
			this.value = (String[]) attributes.get("value");
			this.connection = (String) attributes.get("connection");
		}

		public DatabaseOperation getType() {
			return this.type;
		}

		public String[] getValue() {
			return this.value;
		}

		public String getConnection() {
			return this.connection;
		}

		public static  Collection get(Annotations annotations) {
			List annotationAttributes = new ArrayList();
			for (T annotation : annotations) {
				annotationAttributes.add(new AnnotationAttributes(annotation));
			}
			return annotationAttributes;
		}

	}

	private static class Annotations implements Iterable {

		private final List classAnnotations;

		private final List methodAnnotations;

		private final List allAnnotations;

		public Annotations(DbUnitTestContext context, Class container, Class annotation) {
			this.classAnnotations = getAnnotations(context.getTestClass(), container, annotation);
			this.methodAnnotations = getAnnotations(context.getTestMethod(), container, annotation);
			List allAnnotations = new ArrayList(this.classAnnotations.size() + this.methodAnnotations.size());
			allAnnotations.addAll(this.classAnnotations);
			allAnnotations.addAll(this.methodAnnotations);
			this.allAnnotations = Collections.unmodifiableList(allAnnotations);
		}

		private List getAnnotations(AnnotatedElement element, Class container,
				Class annotation) {
			List annotations = new ArrayList();
			addAnnotationToList(annotations, AnnotationUtils.findAnnotation(element, annotation));
			addRepeatableAnnotationsToList(annotations, AnnotationUtils.findAnnotation(element, container));
			return Collections.unmodifiableList(annotations);
		}

		private void addAnnotationToList(List annotations, T annotation) {
			if (annotation != null) {
				annotations.add(annotation);
			}
		}

		@SuppressWarnings("unchecked")
		private void addRepeatableAnnotationsToList(List annotations, Annotation container) {
			if (container != null) {
				T[] value = (T[]) AnnotationUtils.getValue(container);
				for (T annotation : value) {
					annotations.add(annotation);
				}
			}
		}

		public List getClassAnnotations() {
			return this.classAnnotations;
		}

		public List getMethodAnnotations() {
			return this.methodAnnotations;
		}

		public Iterator iterator() {
			return this.allAnnotations.iterator();
		}

		private static  Annotations get(DbUnitTestContext testContext,
				Class container, Class annotation) {
			return new Annotations(testContext, container, annotation);
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy