DB Unit Testing framework?
Asked Answered
D

4

7

In my project, I've used spring, jpa with PostgreSQL DB, I've lots of table in DB and I need to have Unit testing of all of them.

Is there any framework which just rollback all the transactions after each test finished so every test will have fresh/same DB data to Test. And this way after all Test executions, data of DB schema would be as it is.

Any suggestion for this?

I've some idea of DBUnit but in that I need to write .xml files for every input data for every test and need to insert data in setup() and clear/remove data in tearDown(), but doesn't seems better strategy to me.

Any suggestion is appreciated. Thanks.

Delude answered 4/8, 2011 at 12:2 Comment(0)
U
3

Is there any framework which just rollback all the transactions after each test finished so every test will have fresh/same DB data to Test. And this way after all Test executions, data of DB schema would be as it is.

From my other answer posted earlier in the day, yes, this is possible using DbUnit. (Based on your edit, you don't need this; the subsequent section of my answer addresses why I use DbUnit, and when I wouldn't use it).

The following code snippet demonstrates how the setup of every test is performed:

@Before
public void setUp() throws Exception
{
    logger.info("Performing the setup of test {}", testName.getMethodName());
    IDatabaseConnection connection = null;
    try
    {
        connection = getConnection();
        IDataSet dataSet = getDataSet();
        //The following line cleans up all DbUnit recognized tables and inserts and test data before every test.
        DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
    }
    finally
    {
        // Closes the connection as the persistence layer gets it's connection from elsewhere
        connection.close();
    }
}

private IDatabaseConnection getConnection() throws Exception
{
    @SuppressWarnings({ "rawtypes", "unused" })
    Class driverClass = Class.forName("org.apache.derby.jdbc.ClientDriver");
    Connection jdbcConnection = DriverManager.getConnection(jdbcURL, "XXX",
            "YYY");
    IDatabaseConnection databaseConnection = new DatabaseConnection(jdbcConnection);
    return databaseConnection;
}

private IDataSet getDataSet() throws Exception
{
    ClassLoader classLoader = this.getClass().getClassLoader();
    return new FlatXmlDataSetBuilder().build(classLoader.getResourceAsStream("database-test-setup.xml"));
}

The database-test-setup.xml file contains the data that will be inserted into the database for every test. The use of DatabaseOperation.CLEAN_INSERT in the setup method ensures that all the tables specified in the file will be cleared (by a delete of all rows) followed by an insert of the specified data in the test data file.

Avoiding DbUnit

I use the above approach specifically to clear out sequences before the start of every test, as the application uses a JPA provider which updates the sequences in a separate transaction. If your application is not doing anything like that, then you can afford to simply start a transaction in your setup() method and issue a rollback on teardown after the test. If my application didn't use sequences (and if I didn't desire to reset them), then my setup routine would have been as simple as:

@Before
public void setUp() throws Exception
{
    logger.info("Performing the setup of test {}", testName.getMethodName());
    // emf is created in the @BeforeClass annotated method
    em = emf.createEntityManager();
    // Starts the transaction before every test
    em.getTransaction.begin();
}

@After
public void tearDown() throws Exception
{
    logger.info("Performing the teardown of test {}", testName.getMethodName());
    if (em != null)
    {
        // Rolls back the transaction after every test
        em.getTransaction().rollback();
        em.close();
    }
}

Also, I use dbdeploy with Maven, but that is primarily for keeping the test database up to date with the versioned data model.

Unthinkable answered 4/8, 2011 at 12:17 Comment(4)
Thanks for reply, its really very helpful but I think with the first approach(DbUnit) there is overhead of Data Insertion as its done for every test during setup() so For example there can be many tests where we need data from a one/two tables but every time setup() will insert data for all the tables.Delude
I think second approach(Transaction) would not save Data in DB (its not doing actual persistence of data) so DB won't throw Constraint violation exceptions and it doesn't seem proper testing for all scenarios. Please let me know if I am wrong in this.Delude
@SmartSolution, you can always have separate XML files for the each fixture. Of course, if each test within the fixture requires only a portion of the data and if this is an overhead, then I would suggest using the Testcase Class per Fixture pattern. On the topic of constraint violations and exceptions thrown, Persistence exceptions will be thrown when you flush the PersistenceContext and not on COMMIT as far as I know.Unthinkable
@SmartSolution, if you are not flushing the persistence context, then consider changing your repository/DAO classes to do so. You can avoid that if you have a set of unit tests that will create the persistence context in a plain Java SE environment and test your DAO/repository behavior - this will not test for transaction propagation etc.Unthinkable
D
4

As @Ryan indicates .... the Testing section of the Spring Reference manual should be consulted.

Some startup tips...

We've handled this using Spring's AbstractTransactionalJUnit4SpringContextTests.

For example, we define an abstract superclass:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:WebContent/WEB-INF/testconfig/test-web-application-config.xml")
@TransactionConfiguration()
@Transactional
public abstract class OurAbstractTransactionalSpringContextTest extends AbstractTransactionalJUnit4SpringContextTests {
}

And then individual subclasses which need additional context get defined as:

@ContextConfiguration("classpath:path/to/config/ConfigForTestCase.xml")
public class TestOurFunction extends OurAbstractTransactionalSpringContextTest {
    @Test
    public void testOurMethod() {
    }

}

Note that:

  1. Not all test classes need additional context for them, skip the @ContextConfiguration on the particular subclass.
  2. We execute via ant and use the forkmode="perBatch" attribute on the junit task. That ensures all tests run with the same context configuration (saves from reloading the Spring context for each test). You can use the @DirtiesContext to indicate that the context should be refreshed after a method/class.
  3. mark each method with the @Test annotation. The Spring framework doesn't pick up methods using Junit's public void testXXX() convention.
Doretha answered 4/8, 2011 at 13:43 Comment(0)
U
3

Is there any framework which just rollback all the transactions after each test finished so every test will have fresh/same DB data to Test. And this way after all Test executions, data of DB schema would be as it is.

From my other answer posted earlier in the day, yes, this is possible using DbUnit. (Based on your edit, you don't need this; the subsequent section of my answer addresses why I use DbUnit, and when I wouldn't use it).

The following code snippet demonstrates how the setup of every test is performed:

@Before
public void setUp() throws Exception
{
    logger.info("Performing the setup of test {}", testName.getMethodName());
    IDatabaseConnection connection = null;
    try
    {
        connection = getConnection();
        IDataSet dataSet = getDataSet();
        //The following line cleans up all DbUnit recognized tables and inserts and test data before every test.
        DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
    }
    finally
    {
        // Closes the connection as the persistence layer gets it's connection from elsewhere
        connection.close();
    }
}

private IDatabaseConnection getConnection() throws Exception
{
    @SuppressWarnings({ "rawtypes", "unused" })
    Class driverClass = Class.forName("org.apache.derby.jdbc.ClientDriver");
    Connection jdbcConnection = DriverManager.getConnection(jdbcURL, "XXX",
            "YYY");
    IDatabaseConnection databaseConnection = new DatabaseConnection(jdbcConnection);
    return databaseConnection;
}

private IDataSet getDataSet() throws Exception
{
    ClassLoader classLoader = this.getClass().getClassLoader();
    return new FlatXmlDataSetBuilder().build(classLoader.getResourceAsStream("database-test-setup.xml"));
}

The database-test-setup.xml file contains the data that will be inserted into the database for every test. The use of DatabaseOperation.CLEAN_INSERT in the setup method ensures that all the tables specified in the file will be cleared (by a delete of all rows) followed by an insert of the specified data in the test data file.

Avoiding DbUnit

I use the above approach specifically to clear out sequences before the start of every test, as the application uses a JPA provider which updates the sequences in a separate transaction. If your application is not doing anything like that, then you can afford to simply start a transaction in your setup() method and issue a rollback on teardown after the test. If my application didn't use sequences (and if I didn't desire to reset them), then my setup routine would have been as simple as:

@Before
public void setUp() throws Exception
{
    logger.info("Performing the setup of test {}", testName.getMethodName());
    // emf is created in the @BeforeClass annotated method
    em = emf.createEntityManager();
    // Starts the transaction before every test
    em.getTransaction.begin();
}

@After
public void tearDown() throws Exception
{
    logger.info("Performing the teardown of test {}", testName.getMethodName());
    if (em != null)
    {
        // Rolls back the transaction after every test
        em.getTransaction().rollback();
        em.close();
    }
}

Also, I use dbdeploy with Maven, but that is primarily for keeping the test database up to date with the versioned data model.

Unthinkable answered 4/8, 2011 at 12:17 Comment(4)
Thanks for reply, its really very helpful but I think with the first approach(DbUnit) there is overhead of Data Insertion as its done for every test during setup() so For example there can be many tests where we need data from a one/two tables but every time setup() will insert data for all the tables.Delude
I think second approach(Transaction) would not save Data in DB (its not doing actual persistence of data) so DB won't throw Constraint violation exceptions and it doesn't seem proper testing for all scenarios. Please let me know if I am wrong in this.Delude
@SmartSolution, you can always have separate XML files for the each fixture. Of course, if each test within the fixture requires only a portion of the data and if this is an overhead, then I would suggest using the Testcase Class per Fixture pattern. On the topic of constraint violations and exceptions thrown, Persistence exceptions will be thrown when you flush the PersistenceContext and not on COMMIT as far as I know.Unthinkable
@SmartSolution, if you are not flushing the persistence context, then consider changing your repository/DAO classes to do so. You can avoid that if you have a set of unit tests that will create the persistence context in a plain Java SE environment and test your DAO/repository behavior - this will not test for transaction propagation etc.Unthinkable
A
2

Spring's test framework does exactly that for you.

Anarchism answered 4/8, 2011 at 12:41 Comment(1)
This is correct but not very specific. See the elaboration by Jacob.Oxysalt
H
0

I'd handled it following ways.

When the project is in test mode. I'd used bootstraping data to to test using dbdeploy Fixed data that you can assert on. and use the dao directly to test the DAO and DB layer of your application.

Hope it helps

Update

for example there is an entity called Person in your system, now what you can test on this is basic CRUD operations.

  • Run bootstraping data scripts to laod the data
  • retrieve all the persons from DB and assert on it. like wise see all the CRUD

to rollback the transaction you can mark

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

so it will rollback the DB stuff

Hebrides answered 4/8, 2011 at 12:6 Comment(3)
with this way, As data is not committed in DB so persitence of data would not be tested and it may not throw some exception of Constraint violation etc. so this Transaction related approach doesn't seem right strategy for all scenarios.Delude
you mean to say, Constraint violation exceptions will be thrown without committing trasaction? If yes, I really don't know this, Can you plz give some idea on this?Delude
@JigarJoshi let us continue this discussion in chatDelude

© 2022 - 2024 — McMap. All rights reserved.