views:

160

answers:

4

What is current good practice for agile integration testing of DAOs against a real database schema in the Java landscape?

(By 'agile' I mean what is the most streamlined, automated & simple solution to this problem).

I hope to achieve automation of tests that prove the data access layer integrates seamlessly with real running instances of a database. It is important that we test our custom SQL against specific vendor DBs. I.E. if we write T-SQL we want to test against SQL Server.

Should a single database be dedicated to all running tests, but with no commits? Or should each test environment have a dedicated database?

How do people deal with setup and teardown? Is DBUnit still in popular use? How could Spring help?

+1  A: 

I think one database(per developer) should be fine as long as you make sure to tear down data you setup at the start of the test. If you are not dependent on any initial set of data, then you can simply clean the tables. DBUnit comes out to be a nice solution in this case.

Sometimes you are dependent on some initial set of data not for all tests but for few tests mainly for select queries. If you are dependent on some set of data, it becomes tricky. You can still manage with Dbunit by running seed data before start of each test and tear down at the end, but it makes tests run for longer duration.

In this case, what I have found better is to create a simple utility class to keep an id of whatever new data have been created at the start of test and just delete it in the end.

Ankit
+2  A: 

I would say that your DAO's integration test need only be concerned with ensuring that the ORM's QL e.g. HQL, or JDBC SQL, is valid. I think that it is reasonable to assume that the query languages themselves have been tested thoroughly against multiple drivers/databases. Following through with this, using an "in memory" database such as HSQLDB is reasonable.

"Spring Test" is aimed at integration testing and is great at setting up an IoC context, providing transactions, setting up a database session, creating a schema and then dropping it post test, rolling back transactions after a test... so much so that I'm sitting on the fence with regards to labelling some of my database tests as integration tests (I personally think that so long as the unit test does not cross process boundaries and that their set up is frictionless, they constitute a reasonable unit test).

A few good System Tests, on the other hand, should test your system end-to-end and with all of the target databases. There's always a great deal of effort in setting up system tests and, because you're testing the system itself, there's much help that is applicable in terms of frameworks and toolkits.

Christopher Hunt
I also strongly recommend using Spring Data as it simplifies the writing of our DAOs.
Christopher Hunt
We are not using an ORM, as the configuration against our vendor's DB schema is an overhead we don't need. Therefore we write our own DAOs and :. we need automated integration tests to ensure our data access layer is correct. We are using Spring's SimpleJDBCTemplate.
Synesso
Writing your own DAOs happens whether you use an ORM or not so I'm not sure that particular comment has relevance. My statements still stand however I will revise them with JDBC considerations.
Christopher Hunt
+2  A: 

It is important that we test our custom SQL against specific vendor DBs

So you should create a suite of test for each vendor DB

@RunWith(Suite.class)
@Suite.SuiteClasses({})
public class SQLServerTestSuite {

    @BeforeClass
    public static void setUpClass() throws Exception {

    }

    @AfterClass
    public static void tearDownClass() throws Exception {
        // clean database
    }

}

Should a single database be dedicated to all running tests, but with no commits ?

It is recommended you perform SQL commands but with rollback command because if using commit you can change the state of the current test as well as change the state of others tests. Otherwise, you can deal with unexpected behavior.

For each developer, it is recommended a database sandbox. This allows each user to modify the database in any way they see fit and to exercise the application with tests without worrying about any interactions between their tests and the tests of other users

How do people deal with setup and teardown ?

public class PersonTest {

    @Before
    public void setUp() {
        // set up state to run the test
    }

    @After
    public void teardown() {
       // Transaction rollback
    }

}

The nice thing about Transaction rollback pattern is that it leaves the database in exactly the same state as when we started the test regardless of what changes we made to the database contents

Is DBUnit still in popular use ?

DBUnit is used to compare the database tables against an XML file that contains the expected values. You should keep in mind DBUnit deal with hand-coded settings. However, although this approach is more thorough it is extremely tedious to develop and maintain these kinds of tests. In addition, the tests do not detect a missing mapping for a newly added field or property.

How could Spring help ?

Spring has built-in support to Transaction rollback pattern and supports named parameter (SqlParameterSource) which allows you to externalize plain jdbc queries in a multiline and readable xml file for each vendor database, for instance

<?xml version="1.0" encoding="UTF-8"?>
<queries>
    <query name="PERSON_BY_ID">
        <![CDATA[
            select 
                *
            from 
                PERSON
            where
                PERSON.ID = :integerId
        ]]>
    </query>
</queries>

Notice the query parameter. It follows JavaScript code style where you declare the kind of parameter as prefix. Now you can create a class where you define the set of named queries

public class SQLServerQuery {

    public static final String PERSON_BY_ID = "PERSON_BY_ID";

}

For dinamic queries, it is usually important to detect bugs in the logic of the query such as using < instead of <=. To catch these kinds of bugs, we need to write tests that populate the database with test data, execute the query, and verify that it returns the expected objects. Unfortunately, these kinds of tests are time consuming to both write and execute.

Arthur Ronald F D Garcia
@Synesso And finally, if possible, use an in-memory database such as H2 - you just need a single jar file "to startup" your in-memory database. You can get h2 jar file here: http://hsql.sourceforge.net/m2-repo/com/h2database/h2/1.2.143/h2-1.2.143.jar
Arthur Ronald F D Garcia
Thanks. I'm using Spring's SimpleJDBCTemplate. How can I enable the "built-in support for Transaction rollback pattern"?
Synesso
Found it. Because we're using SimpleJDBCTemplate the reference to Spring's implementation of txn rollback is appreciated. HSQL wont help us as we need to test both T-SQL and Oracle SQL dialects against the servers themselves.
Synesso
@Synesso See here http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html#testing-tx The Spring way of dealing with Transaction rollback pattern
Arthur Ronald F D Garcia
A: 

Maybe I don't understand the purpose of what you are trying to do or why this test approach was chosen, but have you considered using mock objects? You could write Junits tests using mock objects and integrate it with Hudson for automation. You could mock DAOs, preparedstatements, connections, resultsets and so on.

The DAO is a proven pattern. And so is JDBC or anything else you might use for interfacing. Are you trying to prove that it works? or do you really mean to test the queries or SQL within the methods of the DAO?

sjt
If I mock or stub the database then it is no longer a database integration test. DAO is a pattern, but patterns are not under test - implementations are.
Synesso
My point exactly - "The DAO is a proven pattern. And so is JDBC or anything else you might use for interfacing. Are you trying to prove that it works?" <-- rhetorical question really. That is the soul purpose of using mock objects is for testing implementations and not the services used. There is no disagreement here. Mock objects may not help you with live DB Integration tests agreed, but I think you got the concept of mock objects confused here but we are digressing from the topic.
sjt
Mock vs Live DB - I understand that you need to test against a live database, but I think that testing against a mock database is also a good thing. Why? They test different things. Testing against a mock database tests that the code makes the requests you expect, and handles the data how you expect. On the other hand Testing against the live database verifies that this aligns with how the data really is.
sjt