Home

News: Article: Unit Testing with Hibernate

  1. Article: Unit Testing with Hibernate (42 messages)

    Unit-testing should have as few barriers as possible. For relational databases those barriers range from external dependencies (is the database running?) to speed to keeping the relational schema synchronized with your object model. For these reasons it is vital to keep database access code away from the core object model and to test as much as possible without touching a real database.

    This article shows you how you can unit-test your Hibernate model, with a technique that should work for other O/R mappers as well.

    Read Unit Testing with Hibernate

    Threaded Messages (42)

  2. Hi,

    I'm using Hibernate for DAOs in one project, and iBATIS in another. In both cases I'm doing unit tests of the DAOs. I find it very worthwhile to test the Hibernate/iBATIS mapping configuration.

    These tests might also be described as "integration" tests, because I am running against the real RDBMS. I've considered using HSQL, but haven't so far because I'm worried the difference in SQL dialects would invalidate some aspects of the tests. For example, key generation could be different from my real database.

    I use a test instance of the RDBMS. I use DbUnit extensively to set up my tests, and to verify results.

    DbUnit can "prime" a database with test data from a very straightforward XML file.

    Here's a simple example:

    <dataset>

      <artists/>
      <branches/>
      <catalog_types catalog_type_id="C1000" description="1000 songs" />
      <catalog_types catalog_type_id="C2000" description="2000 songs" />
      <catalog_types catalog_type_id="NONE" description="None" />
    </dataset>

    Using DbUnit's CLEAN_INSERT operation with this file deletes all rows from catalog_types, branches and artists, then inserts the 3 given rows.

    I often put a CLEAN_INSERT with a file listing all or most of my tables into the setUp() method of my test case. Then individual test methods may load further data. This helps isolate tests from each other - one test will not randomly break because of the state the database was left in by a previous test.

    The only difficulty I've had with CLEAN_INSERT is that it can be tricky to get the ordering right for foreign key relationships. (Circular relationships are particularly tricky.)

    DbUnit also provides methods to compare database data with expected data, which you can use to verify results of DAO or other operations. I use this to test DAOs.

    I also use this feature to test non-trivial views in my database. I can set up a very simple DbUnit test case to populate the database with known test data that exercise the view, and then use DbUnit's verification to check that my view actually returns what I want it to. I've frequently had times on projects where a non-trivial view requires rewriting for optimization or other reason, and such unit tests can help verify the rewriting hasn't broken anything.

    DbUnit provides a host of other useful features.

      http://dbunit.sourceforge.net/

    John Hurst

    Wellington, New Zealand
  3. I am glad to share one database unit testing tool. It is named as AnyDbTest (www.anydbtest.com). AnyDbTest Express edition is free of charge. AnyDbTest is declarative style testing tool. We will not need to program at all. What we do is to express what we want to test, rather than how to test. We only need to configure an Xml test file to tell AnyDbTest what we want to test. Rather than painstakingly writing test code for xUnit test framework. So AnyDbTest is the right choice for DBA or DB developers. AnyDbTest also offers a visual dashboard. Success or failure of test is automatically computed and presented to us via an easy-to-understand red/green light display. Features specific to AnyDbTest: *Writing test case with Xml, rather than Java/C++/C#/VB test case code. *Many kinds of assertion supported, such as StrictEqual, SetEqual, IsSupersetOf, Overlaps, and RecordCountEqual etc. *Allows using Excel spreadsheet/Xml as the source of the data for the tests. *Supports Sandbox test model, if test will be done in sandbox, all database operations will be rolled back meaning any changes will be undone. *Unique cross-different-type-database testing, which means target and reference result set can come from two databases, even one is SQL Server, another is Oracle.
  4. Important topic, interesting article.

    As John points out, this is really integration testing, rather than unit testing. Unit testing should test classes in isolation and shouldn't depend on a database.

    That said, I've found this form of integration testing hugely valuable on several projects, whether using Hibernate, JDBC or other data access solutions.

    Some notes:

    - I virtually always use the target database server (a development installation, of course). In the biggest project in which we used this technique, we were doing lots of smarts in Oracle triggers, views and stored procedures and trying to port enough of that to run meaningful tests against another database was unjustifiable. It also wouldn't have tested the stack correctly.

    I don't think using a non-production database for testing is wise or necessary, except perhaps in prototyping. We used connection pooling in our integration tests with Commons DBCP. The supposed overhead of running against Oracle was virtually nonexistent after the pool was running. The saving running against anything else wouldn't have been worth it. (The only downside was that the team needed Oracle installed on their laptops due to some remote working, but that isn't a problem if the whole team are in the same office. Anyway, it's a false economy to give developers machines that can't run real software.)

    - We used the Spring integration testing framework, which has been available since Spring 1.1.1. This is included in the spring-mock.jar in the org.springframework.test package and delivers the key features described in the article and more. Assuming that your application is already using Spring, it provides:
     - automatic creation and rollback of transactions so you don't need cleanup after each test case: this can save a lot of time
     - population of JUnit test cases by Dependency Injection, based on your Spring configuration. No need for explicit lookups in test cases
     - ability to manage Hibernate, JDO, JDBC, TopLink, iBATIS/whatever data access in the same transaction, ensuring correct rollback
    - convenient JDBC helper instance variable to ease verification
     - caching of Hibernate or other configuration for the duration of the entire test suite to avoid repeated hits to parse the metadata

    See the section in the Spring reference manual for documentation. The PetClinic sample app shipped with Spring includes an example using this with Hibernate.

    Rgds
    Rod Johnson
    Spring from the Source
  5. Article: Unit Testing with Hibernate[ Go to top ]

    Hi Rod,

    I should have mentioned, I'm using Spring also in both my current projects, and am a big fan :-)

    I am using the Spring AbstractDependencyInjectionSpringContextTests base class to populate some of the beans in my DAO tests. I do find it a little frustrating that it's necessary to subclass in order to get this ... I haven't worked out a better alternative yet but I'm thinking about it. I don't like having to subclass an arbitrary 3rd party TestCase class, because (1) there are quite a few of them to choose from, and (2) I would prefer to have all my test cases, both unit and integration, extend a project-specific TestCase that I can put all my common project-specific assertions etc into.

    This is one reason for example why I have avoided pursuing JMock and stick with EasyMock instead (for regular unit tests). As I understand it, JMock requires my test classes to extend their test class; EasyMock doesn't.

    I looked at "automatic creation and rollback of transactions so you don't need cleanup after each test" provided by Spring. I've seen this approach advocated by a number of people, but I'm not sold on it. I understand the performance advantage. But to me, it kind of ruins test isolation. If you don't have each test prime the database in a reliable manner, you are risking running against an arbitrary, random database state. For the performance issue, my approach is to place my integration tests in a SlowTestSuite suite, which I don't necessarily run all the time in my IDE, but I _do_ run in my continuous integration tool (CruiseControl). So, the integration tests against the database may take ten minutes or even a little more; I'm still going to get my feedback reasonably quickly if something breaks.

    Having said that, one of my projects uses Firebird for the database, and that's easy and lightweight to run locally on my laptop, so that's what I do. The other project is Oracle, and although I have run Oracle locally on my laptop, it is not that pleasant.

    In either case, local or remote database, I'm pretty sold on DbUnit as a great way to manage and verify test data. The performance on a remote database can be pretty bad though.

    Regards

    John Hurst
    Wellington, New Zealand
  6. Article: Unit Testing with Hibernate[ Go to top ]

    John
    I looked at "automatic creation and rollback of transactions so you don't need cleanup after each test" provided by Spring. I've seen this approach advocated by a number of people, but I'm not sold on it. I understand the performance advantage. But to me, it kind of ruins test isolation. If you don't have each test prime the database in a reliable manner, you are risking running against an arbitrary, random database state.
    First, my personal experience has been that the performance advantage is huge with this kind of approach. So great that you won't want to go back to slower approaches. In a recent project we had around 200 integration tests (in addition to true unit tests), all of which hit the database via an ORM tool or JDBC and exercised a lot of functionality in Java and the database. These tests executed in Eclipse in around a minute--against Oracle--on an average spec developer machine. This made it possible to run the entire integration test suite, as well as unit tests, before committing changes to source control, and without leaving the IDE.

    I understand your reservations about database state. The solution we've adopted is to have a set of reference data loaded by DBUnit or a database import which is the starting point. Then all the tests runs in their own transaction. When each is finished and the transaction is automatically rolled back, there's no impact on database state except a whack of sequence numbers used up. So nothing executes in a random database state, because once the initial seeding is done, the state is not altered by the test suite.
  7. Hi Rod,

    Thanks for your comments. I have to admit I haven't actually tried the rollback approach for myself--I guess I will have to now.

    There are a couple of other concerns I had about the rollback approach, but I guess they are not insurmountable.

    The pattern I've been using for Hibernate testing is to prime in one transaction, run the test code in a second, and verify results in a third. Yes, I can see it seems very expensive compared to the rollback approach. But it did seem straightforward and robust!

    I put explicit transaction management into the test methods in order to keep Hibernate happy with lazy-loading. Now I think that was a mistake; the transaction management should be kept out of the test code (and put in the fixture instead) as a separate concern, just as with production code.

    In my first project with DbUnit I used the "one dataset for all tests" approach. Of course, my mistake was trying to include the real data for the tests into this giant dataset, rather than just the common reference data. I had a lot of problems with different tests' data requirements conflicting with each other. Now I am probably erring the other way in striving for complete isolation.

    Regards

    John Hurst,
    Wellington, New Zealand
  8. John
    I put explicit transaction management into the test methods in order to keep Hibernate happy with lazy-loading.
    Yes, some kind of transaction/connection management smarts is essential to effective integration testing. If you're using HibernateTransactionManager with Spring, of course, you can use the same transaction manager in test as in deployed environment, unless you need JTA for distribution or messaging. The test functionality I mentioned offers an endTransaction() method which enables the transaction to be ended early, which is useful for checking lazy loading behaviour. But in general I agree with your later comment: transaction management should be treated largely as an aspect in test cases as in production code.

    Rgds
    Rod
  9. In my first project with DbUnit I used the "one dataset for all tests" approach. Of course, my mistake was trying to include the real data for the tests into this giant dataset, rather than just the common reference data. I had a lot of problems with different tests' data requirements conflicting with each other. Now I am probably erring the other way in striving for complete isolation.RegardsJohn Hurst,Wellington, New Zealand
    This was a big mistake in our projekt too in the beginning. "One size fits all" does not work for test data. Each business logic test needs its OWN set of data. Otherwise you will have "spaghetti" test data.

    The side effect of "each test creates its own data and deletes it afterwards" is that the database performance is not good. We use Oracle. It would be good if Oracle could act like an in-memory database.
  10. I've been using the rollback approach, and I'm very satisfied with it. In addition, I've found it useful to explicitly flush the Hibernate session periodically, as this forces the SQL to be flushed to the database. Hibernate is smart about hitting the database, and will do so only when it needs to. If you don't flush, your entire test could be running against the level one cache.
    Flushing allows you to catch errors in cascading parent-child operations, if the hibernate mappings are not set up correctly, foreign key constraints, database triggers, etc. Also, if your tests flush the session immediately after any database operation, you're more likely to catch problems closer to where they occur. The down side is that your tests will run marginally slower, which, to me, is not a big deal.
    I tend to go with the more liberal interpretation of "unit test", where the goal is to ensure that a class works correctly in an environment closest to what it was intended for. To this end, I think it is necessary for any persistence related code to actually hit the data store, before I'm convinced that the code works.
    Howard
  11. I've found it useful to explicitly flush the Hibernate session periodically, as this forces the SQL to be flushed to the database. Hibernate is smart about hitting the database, and will do so only when it needs to.
    Good point. I forgot to mention that this is often essential.
    If you don't flush, your entire test could be running against the level one cache.
    Even flushing won't necessarily result in Hibernate requerying if you verify purely using Hibernate. So often it's good to flush then verify using JDBC, rather than (or as well as) Hibernate.
  12. flush & clear[ Go to top ]

    We use setup in transaction. To make sure our test hits the database and not just the L1 cache we flush and then clear the session.
  13. Hi Rod,

    How do you protect against adding data to the reference data that messes up some test that has already been written?

    cheers,

    David
  14. quick way to make the dbunit data[ Go to top ]

    i tend to use dbunit, but found it painful to
    have to make the default data. so i wrote a quick and dirty
    (very quick, very dirty) xsl that reads the *.hbm.xml files and makes a dbunit snippet for each table as:

    <table>
    <column>foo</column>
    <column>bar</column>
    <row>
    <value>TEST-FOO</value>
    <value>TEST-BAR</value
    </row>
    </table>

    I wrote in ten minutes, so it can definitely be expanded to include the entire dtd and do fancy stuff. as it stands now it handles string, text as above, int/long/Integer/Long as 1, date as 2001-01-01 00:00:00, and discriminator columns as
    DISCRIMINATOR. I can edit the data and copy and paste the row sets, but it still saves a bunch of typing and i can regenerate it when the mapping changes. i use a simple ant xslt task to invoke this.

    note sure if code show up here, but here it is (you may need to view source?)

    <code>

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output indent="yes"/>
    <xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
    <xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>

    <xsl:template match="/">
    <dataset><xsl:apply-templates /></dataset>
    </xsl:template>


    <xsl:template name="rows">
    <row>
    <xsl:for-each select="id[@column]">
    <value><xsl:attribute name="column"><xsl:value-of select="./@column" /></xsl:attribute>1</value>
    </xsl:for-each>
    <xsl:for-each select="discriminator[@column]">
    <value><xsl:attribute name="column"><xsl:value-of select="./@column" /></xsl:attribute>DISCRIMINATOR</value>
    </xsl:for-each>
    <xsl:for-each select="property[@column]">
    <value><xsl:attribute name="column"><xsl:value-of select="./@column" /></xsl:attribute>
    <xsl:choose>
    <xsl:when test="contains(./@type, 'String')">TEST-<xsl:value-of select="translate(./@column,$lcletters,$ucletters)"/></xsl:when>
    <xsl:when test="contains(./@type, 'Date') and
    not(contains(./@column, 'deleted'))">2001-01-01 00:00:00</xsl:when>
    <xsl:when test="contains(./@type, 'Date') and
    contains(./@column, 'deleted')"><null /></xsl:when>
    <xsl:when test="contains(./@type, 'Integer')">1</xsl:when>
    <xsl:when test="contains(./@type, 'Long')">1</xsl:when>
    <xsl:when test="contains(./@type, 'int')">0</xsl:when>
    <xsl:when test="contains(./@type, 'text')">TEST-<xsl:value-of select="translate(./@column,$lcletters,$ucletters)"/></xsl:when>
    </xsl:choose></value>
    </xsl:for-each>
    <xsl:for-each select="many-to-one[@column]">
    <value><xsl:attribute name="column"><xsl:value-of select="./@column" /></xsl:attribute>1</value>
    </xsl:for-each>
    </row>
    </xsl:template>

    <xsl:template match="class">
    <table>

    <xsl:attribute name = "name" ><xsl:value-of select="./@table" /></xsl:attribute>
    <xsl:for-each select="id[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>
    <xsl:for-each select="discriminator[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>
    <xsl:for-each select="property[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>

    <xsl:for-each select="many-to-one[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>

    <xsl:call-template name="rows" />

    </table>
    <xsl:apply-templates />
    </xsl:template>



    <xsl:template match="joined-subclass">
    <table>

    <xsl:attribute name = "name" ><xsl:value-of select="./@table" /></xsl:attribute>
    <xsl:for-each select="key[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>

    <xsl:for-each select="property[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>

    <xsl:for-each select="many-to-one[@column]">
    <column><xsl:value-of select="./@column" /></column>
    </xsl:for-each>

    <xsl:call-template name="rows" />
    </table>
    </xsl:template>

    </xsl:stylesheet>
    </code>
  15. Unit testing should test classes in isolation and shouldn't depend on a database.

    Couldn't disagree more. The test should make sure the code works. That's the goal. Isolation without the database is like isolating the testing a method without testing the entire class. It's nearly useless.
  16. Testing certain types of classes without database access--Hibernate DAO implementations for example--is nearly useless but that is not the case for most classes, which can and should be tested in isolation. And it doesn't change the definition of unit testing.

    I think my post made it clear that I agree that testing using a database for Hibernate and other ORM-related code is valuable: it was merely a question of terminology.
  17. <blockqute>Unit testing should test classes in isolation and shouldn't depend on a database.
    Couldn't disagree more. The test should make sure the code works.Rod is talking about unit tests (definition).

    But I agree that unit tests are rather useless when dealing with typical enterprise apps. The code doesn't do much on its own, but instead depends on myriad of external systems to do much of the "work". That's even more evident when dealing with databases (unless you take the intense ORM route, which is another can of worms).
  18. But I agree that unit tests are rather useless when dealing with typical enterprise apps. The code doesn't do much on its own, but instead depends on myriad of external systems to do much of the "work".
    My experience is that unit tests are incredibly useful, in all server-side applications, regardless of size and complexity. With appropriate design, you can limit the number of classes (like Hibernate DAO implementations) that cannot sensibly be tested in isolation. You can easily enough stub normal collaborators. I've experienced no difference in the value of unit tests between plain web applications and large financial applications. In fact in large financial applications, because they're mission critical and the impact of failure could be so costly, it's essential to maximize all forms of testing.

    If you don't know whether each class is behaving correctly, it can get hard to diagnose problems in interactions. Did it correctly call the other classes? It's also easy to duplicate testing, without getting good code coverage.

    But of course unit testing is just part of an overall testing strategy. No amount of unit testing will free you from the need to do lots of integration tests. But good unit tests will enable you to catch many problems earlier, and build a strong regression test suite.

    Rgds
    Rod
  19. Amen to that, sometimes I think these 'purists' forget the goal is working software, to my mind integration testing is testing the links with external 'active' systems, not an essentialy passive system like a database.

  20. Article: Unit Testing with Hibernate[ Go to top ]

    I basically did the same approach as the article proposes but use spring to glue things together. I make sure that each domain object goes through crud operations and any specific hsql or sql in the DAO layer gets tested. I don't know how many times this approach has saved our butts because a developer would just rename a field in the domain object and not update the dependent hsql.

    One thing that Rod is on the ball is that you should test against the target database server (i.e. don't test against mysql if your production is oracle). It will bite you. Even if you use standard sql or hibernate, there are always differences. I've read numerous articles advocating in-memory databases for the DAO testing. Even though it sounds great in theory it will definitely cause you pain later on.
  21. Important topic, interesting article.As John points out, this is really integration testing, rather than unit testing. Unit testing should test classes in isolation and shouldn't depend on a database.

    +1

    Integration tests have their deserved place, indeed (just like other kinds of automated tests e.g.: user-experience tests, using SilkTest), but where a lot of people miss the point is that, even if you come up with the best set up to test with the actual DB, you _must_ still test your units in isolation and write enough of true _unit_ tests, too.

    Quite a number of people say this is either 1) too hard 2) useless. Been there, done that and learned - that kind of thinking comes out of the lack of knowledge, nothing else. When you learn how to do the isolation properly, there will be no doubt left.

    Isolated Unit-Tests and TDD are _very_ important for getting the design right and you can not afford to ignore them. "If they seem too hard for you, remember that ignorance is no excuse, learn how to do these things right and begin using!" - is what I tell my developers. :P

    Dependency Injection, specificly - things that Spring provides and the EasyMocks project are truely invaluable in getting it "the right way". We owe these guys big time.
  22. I think you are advocating:
    'No code will ever work unless you do unit tests'

    Which is obvious lunacy. Given a choice of ONLY doing unit tests or ONLY doing integration tests which would you choose.

    Obviously it's not a choice we have to make. But the answer makes you think (I hope).

    Jonathan
  23. I think you are advocating:'No code will ever work unless you do unit tests'Which is obvious lunacy. Given a choice of ONLY doing unit tests or ONLY doing integration tests which would you choose.Obviously it's not a choice we have to make. But the answer makes you think (I hope).Jonathan
    Vincent Massol categorizes automated tests in 5 major categories. I, usually, like a little bit modified version of this categorization (mostly avoiding amibguosity in naming and making it more clear, at least for me)
    1) Unit Tests - test only unit of code logi, in isolation from the rest of the application. Very fine-grained. Written by programmers.
    2) Functional Tests - coarse-grained tests, validating some functionality of the application from the code-perspective (as opposed to "user tests"). Written by programmers.
    3) Integration Tests - coarse-grained tests that test a public API of an application component from the specific viewpoint of how it integrates in the rest of the application. Written by programmers.
    4) User Tests - tests that check the application functionality from a user perspective (not from the code-level). It's a click-click-works? kind of test that can (and should) also be automated using SilkTest (commercial, very solid) or Selenium (open-source, kinda fresh, yet). These tests are not written by programmers but rather by a QA person/team.
    5) Load tests - checks performance.

    All these tests, except the unit one, take significant amount of time to run. Therefore it is not adviced to run them at every build - that would decrease productivity of a programmer so much that s/he may stop testing completely. Except unit-tests, all other tests should, probabely, be run on dedicated test servers several times a day, in an automated fashion, and only alert programmer if something breaks. Unit tests, from the other hand, should be fast and programmers are adviced to run them at every build.

    Also - only unit-tests have proven to have the special characteristics of aiding in the design process. They are the kind of tests that you mean when talking about the Tist-Driven Development. This is why they are very important.

    Neverthless, if you ask me about the importance (and this is not my view so I am not gonna steal it) - automated user tests are the most important ones. So if you are going to have only one type - have them. Then the functional and integration tests come in the importance hierarchy. But as all of them are coarse-grained, statistically they only cover 70% of your code. And you must be aware of that. Also, as already mentioned, none of them aid in the design process.

    This is why unit-tests are so important, not because they should be the only test types you write.

    So, I am not as lunatic as one may think :)

    What I try to say - all 5 types of tests (+ Porfiling, by the way) are very important and none of them should be ignored if you are serious about the quality of your application.

    cheers
  24. Clarification/addition to the above: functional/integration and user tests are both coarse-grained and, statistically, only cover 70% of the code. Unit Tests are fine-grained and combined with the rest of the tests can cover higher percentage of the code.
  25. I have seen a huge budget drained into consultancy cofers - and their dogma would include commandments such as:

    "If they seem too hard for you, remember that ignorance is no excuse, learn how to do these things right and begin using!"

    The dogma meant that clever people did as they were told and not what was right. The end result was a code base that could not integrate.

    I agree unit tests are a powerful tool, on a par with a good integration/regression test suite.

    Jonathan
  26. I have seen a huge budget drained into consultancy cofers - and their dogma would include commandments such as:"If they seem too hard for you, remember that ignorance is no excuse, learn how to do these things right and begin using!" The dogma meant that clever people did as they were told and not what was right. The end result was a code base that could not integrate.I agree unit tests are a powerful tool, on a par with a good integration/regression test suite.Jonathan
    Sorry, but I do not understand what your point it. Nobody is arguing with you that unit-tests should not be the only kind of tests, on the contrary I said if you can not have all of them, you should better concentrate on User Tests.

    Yet, you keep to argue - what with? :)

    That "dogma" you quoted just tries to reflect the reality that a lot of people try to avoid writing unit-tests because they are hard and lots of developers do not know how to properly do it. I am sorry if it sounded like commandment, but it really is a problem in the industry and ignorance is not an excuse, indeed.
  27. Fair enough.

    :)

    Jonathan
  28. I have seen a huge budget drained into consultancy cofers - and their dogma would include commandments such as:"If they seem too hard for you, remember that ignorance is no excuse, learn how to do these things right and begin using!" The dogma meant that clever people did as they were told and not what was right. The end result was a code base that could not integrate.I agree unit tests are a powerful tool, on a par with a good integration/regression test suite.Jonathan
    Sorry, but I do not understand what your point it. Nobody is arguing with you that unit-tests should not be the only kind of tests, on the contrary I said if you can not have all of them, you should better concentrate on User Tests.Yet, you keep to argue - what with? :)That "dogma" you quoted just tries to reflect the reality that a lot of people try to avoid writing unit-tests because they are hard and lots of developers do not know how to properly do it. I am sorry if it sounded like commandment, but it really is a problem in the industry and ignorance is not an excuse, indeed.

    The other strong argument for unit tests is refactoring. If you haven't got any unit tests then you can't by definition refactor. Most projects I've worked on have had core elements refactored several times even prior to go-live. Unit tests give developers the confidence to carry out major refactoring of both their own code and that of others (reinforcing the belief that every developer is responsible for the entire code base) safe in the knowledge that the code still works, at least well as as it did previously.
  29. Three types of test...[ Go to top ]

    I think it is key to have an object-oriented domain model (i.e. combining both behavior and data). In this way, the (complex) behavior of your application can be tested by unit testing different parts of your domain model, without requiring complex interactions with persistence.

    Second, DAO interfaces should be used to make it easy to use a simple in-memory DAO where your unit test of the domain model requires it.

    This results in three types of test: (1) unit test of your domain model, (2) unit test of your DAOs (e.g. testing your hibernate mapping), and (3) integration (unit) test of the combination. The last type of test will be relatively small comprising only a few tests since all the difficult parts were already tested before.
  30. Three types of test...[ Go to top ]

    I think it is key to have an object-oriented domain model (i.e. combining both behavior and data). In this way, the (complex) behavior of your application can be tested by unit testing different parts of your domain model, without requiring complex interactions with persistence.
    ...

    My thoughts exactly.
  31. Three types of test...[ Go to top ]

    This results in three types of test: (1) unit test of your domain model, (2) unit test of your DAOs (e.g. testing your hibernate mapping), and (3) integration (unit) test of the combination.
    A correction, if you do not mind: current, agreed terminology in the literature and community: Unit Tests are only called tests that test a unit (mostly a class or its method) in isolation from anything else. Testing the DAO layer is a functional or integration test, depending on how you look at it: if you look at your DAO as a function/service of your app, or you look at it as integration between your App code and Database. Both views are valid.
  32. Rollback solution[ Go to top ]

    I don't let tests rollback after they finished.

    I use a so called "EntityGarbageCollector". The created entities are added to it. At the end of the test you simply write: garbageCollector.disposeGarbage().

    The EntityGarbageCollector knows how to delete them.
  33. Rollback solution[ Go to top ]

    We use similar concept like EntityGarbageCollector (we call ObjectMother anyway...). At setUp() , add an Hibernate interceptor to intercept all saved persistent objects. At tearDown(), just delete all intercepted objects in reverse order. Such approach is a little bit slower than rollback solution. But this is quite useful when you need to do transaction-related integration test.
  34. Rollback solution[ Go to top ]

    We use similar concept like EntityGarbageCollector (we call ObjectMother anyway...). At setUp() , add an Hibernate interceptor to intercept all saved persistent objects. At tearDown(), just delete all intercepted objects in reverse order. Such approach is a little bit slower than rollback solution. But this is quite useful when you need to do transaction-related integration test.
    Interesting approach, but I can see some potential problems in complex apps. It's Hibernate-only, for example. What about JDBC updates/stored procedures? Triggers that Hibernate doesn't know about that do additional INSERTS, such as change tracking, on Hibernate operations? With the rollback approach all this works cleanly. All these are real examples from recent client projects, by the way.
  35. Rollback solution[ Go to top ]

    What about JDBC updates/stored procedures? Triggers that Hibernate doesn't know about that do additional INSERTS, such as change tracking, on Hibernate operations? With the rollback approach all this works cleanly.
    This can be a problem, but it isn't in our case.

    We are not allowed to write data with JDBC. Only reading is allowed. For writing data we always have to use our persistance layer, which is a kind of ORM on top of Entity Beans. In order to be independant of the database, we are not allowed to use stored procedures.

    Writing data with JDBC bypasses validation and can be dangerous. You only have very few database constraints like unique, not null, char length and so on. But our validation rules often contain much more constraints.

    I must admit that I had to write data with JDBC (both DDL and DML) ... It was a table which simulated a database view. To remove the data I simply executed a DROP TABLE statement after the test finished.
  36. Rollback solution[ Go to top ]

    We use similar concept like EntityGarbageCollector (we call ObjectMother anyway...). At setUp() , add an Hibernate interceptor to intercept all saved persistent objects. At tearDown(), just delete all intercepted objects in reverse order. Such approach is a little bit slower than rollback solution. But this is quite useful when you need to do transaction-related integration test.

    And, when debugging, you see the intermediate results in the database with a tool like Toad. Only if data are committed, Toad shows the data. DBVisualizer let you change the connection to dirty read, but it didn't work in my case.

    We don't use Hibernate (instead we use a bad home grown persistance framework on top of Entity Beans). So some things might be different.
  37. Rollback solution[ Go to top ]

    And, when debugging, you see the intermediate results in the database with a tool like Toad. Only if data are committed, Toad shows the data.
    True. However, you can easily verify using JDBC calls within the same transaction, before it gets rolled back. This has the advantage that it's verified by JUnit, whereas with Toad you're reliant on visual inspection. Of course Toad is a wonderful tool overall...couldn't live without it. But I like total automation for testing.

    Rgds
    Rod
  38. Rollback solution[ Go to top ]

    Of course Toad is a wonderful tool overall...couldn't live without it. But I like total automation for testing.RgdsRod
    I like total automation too for testing and I do it this way. But sometimes I have to debug my tests. I suggested Quest to improve Toad, so that I can dirty read for debugging.
  39. Rollback solution[ Go to top ]

    I suggested Quest to improve Toad, so that I can dirty read for debugging.
    If You work with Oracle database then its impossible
    due to "consistent read" isolation level ;)
    Just my 0.05$
  40. As a big time proponent of standalone (read -no server running-) kind of unit tests, I am sad to report that using HSQLDB for db access code has limitations. Especially if you are using complex HQL joins which translate into complex SQL joins, HQL barfs where you would have no problem with MySQL DB or more hard core databases. So I have modified our development environment setup so that now, it is mandatory to have a MySQL server running for every developer (on their local machine). This is not ideal but it at least conforms to KISS principle.
  41. DbUnit can be very useful for managing a database test environment. This applies whether you're using it for Hibernate or any other data access technology.

    It can also be useful for testing SQL queries, although it doesn't have support for parameterised SQL. You can't for example test SQL statements which have bind variables populated using PreparedStatement.setXXX() methods. This means that you can't really test your JDBC based DAOs with it, unless you're prepared to indulge in some trickery

    Here's something which would really take DbUnit to the next level in unit testing Hibernate.

    DbUnit has a the class
    public class Assertion
    {
        public static void assertEquals(ITable expected, ITable actual)
        public static void assertEquals(IDataSet expected, IDataSet actual)
    }

    which allows you to compare data from various sources, including a set of database, an SQL query, or the contents of an XML file.

    Wouldn't it be great to have a mechanism to convert the result of a Hibernate query (a List containing an object graph) into an IDataSet object. You could then compare the Hibernate IDataSet with a fixture FlatXMLDataSet.

    This conversion process should be configurable to NOT do any lazy fetching, so that the Hibernate IDataSet would only represent data returned from the 1st query (and not any lazily initiated subsequent queries).

    This would allow you to do precise, concise and comprehensive tests of Hibernate queries with fairly minimal effort, and if implemented correctly, would properly test your fetch strategy implementation.

    I might take a punt at implementing this myself when I have some time, but if anyone has tried this before it would save me the effort.

    Cheers,
    Phil Zoio
  42. Article: Unit Testing with Hibernate[ Go to top ]

    Wouldn't it be great to have a mechanism to convert the result of a Hibernate query (a List containing an object graph) into an IDataSet object. You could then compare the Hibernate IDataSet with a fixture FlatXMLDataSet.
    Could be useful, but you would probably need to understand the Hibernate mapping metadata. How would you handle complex UserTypes for example?

    Why not simply flush and then use JDBC to query, in the same transaction. That way you don't need to worry about metadata; both sides of your comparison are in the RDBMS representation.
  43. Article: Unit Testing with Hibernate[ Go to top ]

    Could be useful, but you would probably need to understand the Hibernate mapping metadata. How would you handle complex UserTypes for example?Why not simply flush and then use JDBC to query, in the same transaction. That way you don't need to worry about metadata; both sides of your comparison are in the RDBMS representation.

    For testing write operations you could use session.flush(), then use DbUnit to validate changes - no problem.

    For testing read operations and validating the data Hibernate is reading, using the metadata was the first way that came to mind, but definitely involves a lot of work.

    The easier way is to intercept JDBC calls made by Hibernate, and use the Hibernate-generated SQL to build an IDataSet instance.

    Writing tests could be done as follows:
    1) create the Hibernate query and put it in your DAO
    2) set up and load your test data
    3) figure out the SQL that your Hibernate query should execute (most easily done by turning on Hibernate SQL logging and tweaking the HQL/Criteria until you get the SQL you're looking for.
    4) use this SQL to generate a DbUnit XML data set file (e.g. "fixture.xml") from your test data
    5) write your test. Your test will look something like this

    public void testHibernateQuery()
    {

    //assume you've created session
    //and instantiated DAO

    HibernateDataSetCreator creator
    = new HibernateDataSetCreator(session);

    dao.setSession(session);
    List data = dao.getData(... query params here ...);

    IDataSet fixtureData =
    new FlatXmlDataSet("fixture.xml");
    IDataSet hibernateData = creator.getDataSet();
    Assertion.assertEquals(fixtureData, hibernateData);

    }

    The important step is 3) - here's where you figure out what data your query should be returning to appropriately implement your fetch strategy.

    Regarding implementation, the HibernateDataSetCreator would need to hook into the Hibernate/JDBC interface. This could be done in a couple of ways:
    - using AOP
    - replacing the JDBC driver with a proxy (a la p6spy)
    - within Hibernate itself - provide a decorator for Hibernate's Batcher implementation, which could intercept JDBC calls. You'd need to tweak the SessionFactory configuration mechanism and override a couple of Hibernate internal classes to do it this way.