8

I am new to automated testing and dbUnit. So I would appreciate your advice.

I am going to create a test suite, that will run the following way:

  • create an in-memory H2 database
  • run DDL scripts to create tables
  • run dbUnit to insert initial data (let's call it STATE0) that will be used by all tests.
  • run tests

Till there it looks nice for me, but what I don't understand, is how do I revert the database to the STATE0 after a test run and changed the data?

Can I do it with dbUnit?
Or with something else?
Should I recreate the database before each test?

Simple not commiting transactions in tests is not appropriate for me, because the tests will eventually run more than one transaction over may be more than one database connection.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
Igor Mukhin
  • 15,014
  • 18
  • 52
  • 61

2 Answers2

7

DBUnit can do the work four you automatically if you write your @BeforeClass, @Before and @After methods properly. E.g. in our project, using Derby, one such test case looks like

public class MyTest {
    protected static IDataSet getDataSet() throws Exception {
        URL url = MyTest.class.getClassLoader().getResource("MyDataSet.xml");
        return new XmlDataSet(new FileInputStream(url.getPath()));
    }

    private static JdbcDatabaseTester databaseTester;

    @BeforeClass
    public static void setUpClass() throws Exception {
        // Init test environment, session etc.
        databaseTester = new JdbcDatabaseTester(
                "org.apache.derby.jdbc.ClientDriver",
                "jdbc:derby://localhost:1527/myschema", 
                "username", "password");
        databaseTester.setDataSet(getDataSet());
    }

    @AfterClass
    public static void tearDownClass() {
        // Close session etc.
    }

    @Before
    public void setUp() throws Exception {
        databaseTester.onSetup();
    }

    @After
    public void tearDown() throws Exception {
        databaseTester.onTearDown();
    }

    @Test
    public void test() throws Exception { ... }
}

This code puts back (a subset of) the DB schema to the state defined by MyDataSet.xml after each test. (Note that, as @Pascal commented, the reset may not always be full - if a test modifies a table which is not in the dataset, it won't be affected by the @Before / @After methods.)

Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • 1
    Does this really "reset" the DB? I mean, if my test insert some data in the table `FOO` and if `MyDataSet.xml` doesn't include `FOO`, `FOO` won't be "reseted", right? – Pascal Thivent Sep 28 '10 at 15:24
  • @Pascal, you are probably right. Being human, it is always advisable to double-check that if I am running tests which involve table `FOO`, that table is actually included in the dataset. – Péter Török Sep 28 '10 at 15:31
  • éter I agree with that. It's just that the nitpicker in me was not fully convinced by the phrasing of the last sentence :) +1 anyway. – Pascal Thivent Sep 28 '10 at 15:35
  • @Pascal, in our profession it is almost always better to nitpick in advance than to be sorry afterwards :-) So thanks for your comment, I updated my answer to make it (hopefully) clearer. – Péter Török Sep 28 '10 at 15:41
5

To initialize the database to the initial dataset, just implement these methods in your test case :

@Override
protected DatabaseOperation getSetUpOperation() throws Exception
{
    return DatabaseOperation.CLEAN_INSERT; // by default (will do DELETE_ALL + INSERT)
}

@Override
protected DatabaseOperation getTearDownOperation() throws Exception
{
    return DatabaseOperation.NONE; // by default
}

You may have foreign keys constraints if some of your tests insert rows in an empty table (not defined in initial dataset for example).

Just add this empty table in your dataset without any row :

<mydb_mypopulatedtable id="1" name="toto" alias="funky"/>
<mydb_mypopulatedtable id="2" name="titi" alias="groovy"/>
<mydb_mypopulatedtable id="3" name="tutu" alias="creepy"/>

<mydb_myemptytable />

Here, myemptytable has a foreign key to mypopulatedtable. If myemptytable was not defined, DBUnit would try to delete the mypopulatedtable but will fail because of the constraint. If defined, DBUnit will delete myemptytable rows before.

Zofren
  • 1,190
  • 2
  • 11
  • 22