/* * Copyright 2013 The Sculptor Project Team, including 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 org.sculptor.framework.test; import java.sql.SQLException; import javax.annotation.Resource; import javax.persistence.Table; import javax.sql.DataSource; import org.dbunit.database.DatabaseConfig; import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.dataset.IDataSet; import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.sculptor.framework.context.ServiceContext; import org.sculptor.framework.context.ServiceContextFactory; import org.sculptor.framework.util.FactoryConfiguration; import org.sculptor.framework.util.db.DbUnitDataSourceUtils; import org.sculptor.framework.util.db.HsqlDataTypeFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; /** * Base class for transactional spring-based DBUnit tests. * * <p> * Override the method {@link #getDataSetFile} to specify XML file with DBUnit * test data. * * @author Patrik Nordwall * @author Oliver Ringel */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:applicationContext-test.xml" }) @TransactionConfiguration(transactionManager = "txManager", defaultRollback = true) @Transactional public abstract class AbstractDbUnitAnnotationAwareTransactionalTests extends AbstractTransactionalJUnit4SpringContextTests { private final Logger log = LoggerFactory.getLogger(getClass()); public AbstractDbUnitAnnotationAwareTransactionalTests() { } static { ServiceContextFactory.setConfiguration(new FactoryConfiguration() { public String getFactoryImplementationClassName() { return "org.sculptor.framework.context.JUnitServiceContextFactory"; } }); } private final ServiceContext serviceContext = ServiceContextFactory.createServiceContext("JUnit"); private JdbcTemplate jdbcTemplate; protected ServiceContext getServiceContext() { return serviceContext; } /** * inject the datasource */ @Override @Resource(name = "hsqldbDataSource") public void setDataSource(DataSource dataSource) { super.setDataSource(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource); } /** * setup dbunit DatabaseTester/DataSet in transaction * * @throws Exception */ @Before public void setUpDatabaseTester() throws Exception { buildSchema(); IDataSet dataSet = getDataSet(); String[] compositeDataSetFileNames = getCompositeDataSetFiles(); if(dataSet != null) { DbUnitDataSourceUtils.setUpDatabaseTester(getClass(), getJdbcTemplate().getDataSource(), dataSet); } else if (compositeDataSetFileNames != null) { DbUnitDataSourceUtils.setUpDatabaseTester(getClass(), getJdbcTemplate().getDataSource(), compositeDataSetFileNames); } else { DbUnitDataSourceUtils.setUpDatabaseTester(getClass(), getJdbcTemplate().getDataSource(), getDataSetFile()); } restartSequence(); } /** * Override this method to specify a DataSet to use for test data. * If dataSet is not set, getCompompositeDataSetFiles() and getDataSetFile() will be called. * * @return Data set to use */ protected IDataSet getDataSet() { return null; } /** * Start the id sequence from a high value to avoid conflicts with test * data. You can define the sequence name with {@link #getSequenceName}. */ protected void restartSequence() { String sequenceName = getSequenceName(); if (sequenceName == null) { return; } try { DbUnitDataSourceUtils.restartSequence(getConnection(), sequenceName); } catch (Exception e) { log.debug("Couldn't restart sequence: " + sequenceName); } } /** * increase the version number to test optimistic locking * * @param domainObjectClass */ protected void increaseVersion(Class<?> domainObjectClass, Long id) { increaseVersion(getTableName(domainObjectClass), id); } /** * increase the version number to test optimistic locking * * @param tableName * @return */ protected void increaseVersion(String tableName, Long id) { getJdbcTemplate().update("update " + tableName + " set version = version + 1 where id = " + id); } /** * In case you don't need to start the id sequence from a high value to * avoid conflicts with test data you should override this method and return * null. */ protected String getSequenceName() { return null; } /** * Execute some SQL scripts before setup the database to modify the schema * This is used for workarounds * * @throws Exception */ protected void buildSchema() { } @After public void tearDownDatabaseTester() throws Exception { DbUnitDataSourceUtils.tearDownDatabaseTester(); } /** * Override this method to specify the XML file with DBUnit test data. If * filename is not set, DbUnitDataSourceUtils will guess a filename. * * @return the filename with test data */ protected String getDataSetFile() { return null; } /** * Override this method to specify multiple XML files with DBUnit test data to be processed as a CompositeDataSetFile. * If filename is not set, getDataSetFile() will be called. * * @return Array of filenames with test data */ protected String[] getCompositeDataSetFiles() { return null; } protected int countRowsInTable(Class<?> domainObjectClass) throws Exception { return countRowsInTable(domainObjectClass, ""); } /** * Counts the number of rows from a table via jdbc. Table name is picked for @Table * annotation of the domainObjectClass * * @param domainObjectClass * persistent class defining the name of the table for counting * rows * @param condition * additional condition * @return number of rows */ protected int countRowsInTable(Class<?> domainObjectClass, String condition) throws Exception { return countRowsInTable(getTableName(domainObjectClass), condition); } /** * counts the number of rows from a table via jdbc * * @param tableName * name of the table for counting rows * @return number of rows */ @Override protected int countRowsInTable(String tableName) { return countRowsInTable(tableName, ""); } /** * counts the number of rows from a table via jdbc * * @param tableName * name of the table for counting rows * @param condition * additional condition * @return number of rows */ protected int countRowsInTable(String tableName, String condition) { Number number = getJdbcTemplate().queryForObject("select count(*) from " + tableName + " " + condition, Integer.class); return (number != null ? number.intValue() : 0); } protected IDatabaseConnection getConnection() throws Exception { IDatabaseConnection connection = new DatabaseConnection(getJdbcTemplate().getDataSource().getConnection()); DatabaseConfig config = connection.getConfig(); config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqlDataTypeFactory()); return connection; } protected void logDb() { IDatabaseConnection connection = null; try { connection = getConnection(); DbUnitDataSourceUtils.logDb(connection); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { if (connection != null) { try { connection.close(); } catch (SQLException ignore) { } } } } /** * Get the table name from a domainobject class * * @param domainObjectClass * @return the table name * */ protected String getTableName(Class<?> domainObjectClass) { String table = null; if (domainObjectClass.isAnnotationPresent(Table.class)) { table = domainObjectClass.getAnnotation(Table.class).name(); } else { table = domainObjectClass.getSimpleName(); } return table; } protected ApplicationContext getApplicationContext() { return applicationContext; } /** * Return the JdbcTemplate that this base class manages. */ public final JdbcTemplate getJdbcTemplate() { return this.jdbcTemplate; } }