Skip to content

Introduce explicit Hibernate and/or DbUnit support in the TestContext Framework [SPR-3096] #7783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Jan 31, 2007 · 5 comments
Assignees
Labels
in: test Issues in the test module status: duplicate A duplicate of another issue type: enhancement A general enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 31, 2007

nicolas de loof opened SPR-3096 and commented

I'm using this abstract base test class to test my Hibernate mappings and DAOs. It uses AbstractTransactionalDataSourceSpringContextTests to load the Hibernate SessionFactory and adds DbUnit integration to set up the database in an expected state before the test starts.

As I've found it really useful I'd like to contribute it.

public abstract class AbstractHibernateMappingsTests
    extends AbstractTransactionalDataSourceSpringContextTests {

    private HibernateTemplate hibernateTemplate;

    private String schema;

    protected final void onSetUpInTransaction() throws Exception {
        String resource = getDataSetResource();
        if ( resource != null ) {
            logger.info( "Loading DBUnit file " + resource );
            IDatabaseConnection connection = getDatabaseConnection();
            InputStream stream = getClass().getResourceAsStream( resource );
            IDataSet dataSet = new FlatXmlDataSet( stream );
            ReplacementDataSet replacementDataSet = new ReplacementDataSet( dataSet );
            replacementDataSet.addReplacementObject( "[NULL]", null );
            DatabaseOperation.REFRESH.execute( connection, replacementDataSet );
        }
    }

    protected IDatabaseConnection getDatabaseConnection() throws SQLException {
        Connection cnx = hibernateTemplate.getSessionFactory().getCurrentSession().connection();
        IDatabaseConnection connection = new DatabaseConnection( cnx, schema );
        return connection;
    }

    /** 
     * @return a resource name for a DBUnit flatDataset file (or null)
     */
    protected abstract String getDataSetResource();

    public void setHibernateTemplate( HibernateTemplate hibernateTemplate ) {
        this.hibernateTemplate = hibernateTemplate;
    }

   public void setSchema( String schema ) {
        this.schema = schema;
   }

    /**
     * @return the hibernateTemplate
     */
    protected HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }
}

Affects: 2.0.2

Issue Links:

@spring-projects-issues
Copy link
Collaborator Author

nicolas de loof commented

I also added those methods for checking results. This makes Hibernate + DBUnit testing really easy :

    protected IDataSet getDataSet( String resource )
        throws Exception
    {
        IDatabaseConnection connection = getDatabaseConnection();
        InputStream stream = getClass().getResourceAsStream( resource );
        IDataSet dataSet = new FlatXmlDataSet( stream );
        ReplacementDataSet replacementDataSet = new ReplacementDataSet( dataSet );
        replacementDataSet.addReplacementObject( "[NULL]", null );
        return replacementDataSet;
    }

    protected void assertEquals( ITable expected, ITable actual )
        throws Exception
    {
        // Ignore columns not present in "expected"
        Column[] columns = expected.getTableMetaData().getColumns();
        ITable filteredTable = DefaultColumnFilter.includedColumnsTable( actual, columns );
        Assertion.assertEquals( expected, filteredTable );
    }

    protected void assertEquals( ITable expected, String sql )
        throws Exception
    {
        String tableName = expected.getTableMetaData().getTableName();
        ITable actual = getDatabaseConnection().createQueryTable( tableName, sql );
        assertEquals( expected, actual );
    }

@spring-projects-issues
Copy link
Collaborator Author

Arnaud Cogoluègnes commented

Here is a DbUnit integration that leverages the TestExecutionListener feature of the Spring TestContext Framework. It basically injects a DataSet before each test method.

The listener:

import javax.sql.DataSource;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.util.StringUtils;

public class DbUnitExecutionListener implements TestExecutionListener {	
	
	@Override
	public void beforeTestMethod(TestContext testContext) throws Exception {
		DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class);
		
		Resource dataSetResource = getDataSetLocation(testContext);
		if(dataSetResource == null) {
			dataSetResource = getDefaultDataSetLocation(testContext);
		}
		
		if(dataSetResource != null) {
			IDataSet dataSet = new FlatXmlDataSet(dataSetResource.getInputStream());
			dataSet = decorate(dataSet);
			DatabaseDataSourceConnection datasourceConnection = new DatabaseDataSourceConnection(
					dataSource
			);
			DatabaseOperation.CLEAN_INSERT.execute(
					datasourceConnection,
					dataSet
			);
		}
	}
	
	/**
	 * Get explicit dataset location, default uses the DataSetLocation annotation.
	 * @param testContext
	 * @return
	 */
	protected Resource getDataSetLocation(TestContext testContext) {
		DataSetLocation dsLocation = testContext.getTestInstance().getClass().getAnnotation(DataSetLocation.class);
		if(dsLocation != null) {
			return new ClassPathResource(dsLocation.value());
		}
		return null;
	}
	
	/**
	 * Get default dataset location, based on test class name + '-dataset.xml'.
	 * @param testContext
	 * @return
	 */
	protected Resource getDefaultDataSetLocation(TestContext testContext) {
		String dataSetLocation = testContext.getTestInstance().getClass().getName();
		dataSetLocation = StringUtils.replace(dataSetLocation, ".", "/");
		dataSetLocation = "/"+dataSetLocation+"-dataset.xml";
		if(getClass().getResourceAsStream(dataSetLocation) != null) {
			return new ClassPathResource(dataSetLocation);
		}
		return null;
	}
	
	/**
	 * Override to modify the dataset (e.g. use DbUnit's ReplacementDataSet for substitution).
	 * @param dataSet
	 * @return
	 */
	protected IDataSet decorate(IDataSet dataSet) {
		return dataSet;
	}

	@Override
	public void afterTestClass(TestContext testContext) throws Exception { }

	@Override
	public void afterTestMethod(TestContext testContext) throws Exception { }

	@Override
	public void beforeTestClass(TestContext testContext) throws Exception { }

	@Override
	public void prepareTestInstance(TestContext testContext) throws Exception { }

}

Just like the test framework, it uses a default ([TestClass]-dataset.xml) to locate the dataset. To trigger the listener in a test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({DbUnitExecutionListener.class,DependencyInjectionTestExecutionListener.class})
public class PrincipesTest  { 
  // test code
}

The listener can use the @DataSetLocation annotation to locate the dataset if the default location is not ok:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({DbUnitExecutionListener.class,DependencyInjectionTestExecutionListener.class})
@DataSetLocation("/my/path/to/dataset.xml")
public class PrincipesTest  {
  // test code
}

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Have you guys looked at Carbon Five's custom DbUnit TestExecutionListener?

Does the DataSetTestExecutionListener meet your needs?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Please see the Spring Test DbUnit project by Phil Webb and the related blog article.

I think you will find that it meets your needs!

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Sep 29, 2014

Sam Brannen commented

This issue is superseded by #11259.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: duplicate A duplicate of another issue type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants