-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Using JUnit to test Hibernate-dependent code. Suggestions?
PostPosted: Fri Jan 26, 2007 6:11 pm 
Newbie

Joined: Fri Jan 26, 2007 5:47 pm
Posts: 3
Does anyone have any suggestions (or pointers to) in regards to testing Hibernate-dependent applications using JUnit? I’m having trouble figuring out exactly HOW I should be testing my application. Let me explain…

I have a series of Hibernate objects I’ve created to represent my data, and it works great! I can persist the objects, load them back, query, etc with no issues. Everything works great here so far.

The code will be used for both a ‘Spring Framework’ application as well as a simple Java app. I’m working on some simple DAO classes I can use with Spring, but also leverage them in my other application(s) as well. This is where it gets interesting, and I’m having some trouble…

Assume I have an object named ‘Person’. I have another class called ‘PersonManager’, which provides methods such as ‘createPerson’, ‘updatePerson’, ‘findPerson’, ‘getPersonById’, and ‘deletePerson’. Each of these routines contains the code that uses Hibernate to perform the requested function.

When I write my JUnit tests, I’d think that I want to test each of these methods individually. I’d want to make sure I can create a person, update a person, etc… This would allow me to test each method individually to make sure they are all OK. But therein lies the issue…

If I run ‘updatePerson’, I’d have to (1) find the object like getPersonById, (2) make the update, and (3) find the object again to make sure it really updated. So one JUnit test would really be testing TWO separate sets of functionality right? If this is the case, then why test getPersonById individually?

Same with tests such as ‘createPerson’… Should I trust that it worked because I got an ID back? Or should I go out there and LOOK at the fields to make sure they’re right?

I had originally thought about using data from previous tests in later tests, but we don’t have control over the ORDER of the JUnit tests. So that puts a wrench in that idea.

So ultimately, I’m looking for some best practices here… How do you handle testing code similar to this? What advice would you have?

Thanks all!


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 27, 2007 3:14 pm 
Newbie

Joined: Wed Sep 13, 2006 2:44 pm
Posts: 6
Location: San Diego, CA
It is definitely worthwhile making sure your tests are independent and that they don't assume any particular database state to begin with.

Here is a quick rundown of what we do:

* We use Spring's AbstractTransactionalSpringContextTests as our base test class. This ensures your tests run in a transaction. Furthermore it always rolls back at the end of the test. So in actuality you can delete all data from all tables (they provide a convenience method to do this) before running your tests. Spring then rolls-back (including your deletes). Bonus: it also can autowire your DAO/Manager classes into the test. It also ensures that the ApplicationContext is initialized only once for all test methods, rather than once per test.

* You are right that setting up test data using the same classes you are testing does seem a little "dirty". You could use DBUnit to completely separate the test setup. Or you could use the Hibernate Session methods directly. However, in practice, we do use the DAO/Manager methods to setup our test data.

* If you do happen to use other methods to setup your test data, don't assume that means you can skip testing those methods directly. For example, while you might call createPerson() and getPersonById() before testing update, you won't have tested all facets of those methods. You'll want to test getPersonById() with an existing and non-existing PK. You'll want to test createPerson() with and without not null fields filled in.

* If you are going to be setting up your test data using Hibernate and within the same transaction as the tests themselves you should be sure to call flush() and clear() on the session after setting up the data. This ensures the SQL is really executed and the Session-cache is cleared. Otherwise, your tests might not even generate any SQL or hit the database.

* I also recommend executing flush() and clear() before verifying the results of your test for the same reason.

* In terms of verification: while checking all fields of the object would be ideal, in practice I'd just test for equality. If during persistence you manipulate certain fields (like inserting timestamps etc.) you'd want to check those too. Checking the PK is almost certainly not enough because often that value is filled in even if a load fails - you must access another field on the object to cause Hibernate to throw an exception.

* Finally, if you're going to be sharing these DAO classes with other apps, create something like a dao-beans.xml with all DAOs defined with a dependency on a SessionFactory bean that you _don't_ provide. The other users of your library will then get the bean definitions for free - they simply have to ensure they have their correct SessionFactory available in the application context with the appropriate bean name.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 12:52 pm 
Newbie

Joined: Fri Jan 26, 2007 5:47 pm
Posts: 3
timmorrow wrote:
* We use Spring's AbstractTransactionalSpringContextTests as our base test class. This ensures your tests run in a transaction. Furthermore it always rolls back at the end of the test. So in actuality you can delete all data from all tables (they provide a convenience method to do this) before running your tests. Spring then rolls-back (including your deletes). Bonus: it also can autowire your DAO/Manager classes into the test. It also ensures that the ApplicationContext is initialized only once for all test methods, rather than once per test.


In my case, I’ve tried to eliminate the ties between my Hibernate code and Spring. Since I have plans for at least one application that will not be Spring based, I want to make sure I’m not muddying the waters.

timmorrow wrote:
* You are right that setting up test data using the same classes you are testing does seem a little "dirty". You could use DBUnit to completely separate the test setup. Or you could use the Hibernate Session methods directly. However, in practice, we do use the DAO/Manager methods to setup our test data.


I appreciate the pointer to DBUnit. I am reviewing the package now and think it might work out fairly well. Firing out a bunch of SQL before/after the test was what I was thinking, but really wanted to find a way to automate it as well. This should work nicely!

timmorrow wrote:
* If you do happen to use other methods to setup your test data, don't assume that means you can skip testing those methods directly. For example, while you might call createPerson() and getPersonById() before testing update, you won't have tested all facets of those methods. You'll want to test getPersonById() with an existing and non-existing PK. You'll want to test createPerson() with and without not null fields filled in.


You make a very good point here that I didn’t think about… While the data being passed between routines is fairly predictable, the data being passed directly into the methods from outside code (i.e my applications) might not be. So error conditions I address in the other routines won’t show up as they would if I were randomly calling methods. Good advice!

timmorrow wrote:
* If you are going to be setting up your test data using Hibernate and within the same transaction as the tests themselves you should be sure to call flush() and clear() on the session after setting up the data. This ensures the SQL is really executed and the Session-cache is cleared. Otherwise, your tests might not even generate any SQL or hit the database.

* I also recommend executing flush() and clear() before verifying the results of your test for the same reason.


Being a Hibernate newbie, I wasn’t sure if I should do that or not. I had planned to do so (just to make sure), so your recommendation makes me feel a little better. Thanks!

timmorrow wrote:
* In terms of verification: while checking all fields of the object would be ideal, in practice I'd just test for equality. If during persistence you manipulate certain fields (like inserting timestamps etc.) you'd want to check those too. Checking the PK is almost certainly not enough because often that value is filled in even if a load fails - you must access another field on the object to cause Hibernate to throw an exception.


That’s what I figured as well. Thanks!

timmorrow wrote:
* Finally, if you're going to be sharing these DAO classes with other apps, create something like a dao-beans.xml with all DAOs defined with a dependency on a SessionFactory bean that you _don't_ provide. The other users of your library will then get the bean definitions for free - they simply have to ensure they have their correct SessionFactory available in the application context with the appropriate bean name.


I’m not familiar with dao-beans.xml you are referring to… Can you give a pointer to this? I’m including the Hibernate XML mappings in the JAR file, is this what you mean?

All in all, I appreciate the response! It contains a bunch of good advice and makes me feel a bit better about the direction I’m heading in! Thank you very much!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 1:02 pm 
Newbie

Joined: Wed Sep 13, 2006 2:44 pm
Posts: 6
Location: San Diego, CA
Quote:
I’m not familiar with dao-beans.xml you are referring to… Can you give a pointer to this? I’m including the Hibernate XML mappings in the JAR file, is this what you mean?


All I really meant was that to help the clients of your DAO JAR you can include a Spring beans file in the JAR file with all the beans already defined. Saves the clients having to define all the DAOs as Spring beans themselves. All they have to do is make sure to include a reference to that bean file when creating their own ApplicationContext. Then they can define references to those DAO beans from their other beans. The name "dao-beans.xml" was just something I made up!

Tim


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 29, 2007 2:19 pm 
Newbie

Joined: Fri Jan 26, 2007 5:47 pm
Posts: 3
timmorrow wrote:
All I really meant was that to help the clients of your DAO JAR you can include a Spring beans file in the JAR file with all the beans already defined. Saves the clients having to define all the DAOs as Spring beans themselves. All they have to do is make sure to include a reference to that bean file when creating their own ApplicationContext. Then they can define references to those DAO beans from their other beans. The name "dao-beans.xml" was just something I made up!


Ah, I get it now! Sorry, I guess I couldn't see past the filename :-)...

Good advice. I've done a small amount of Hibernate with Spring and it took me a while to get everything wired together. My next step was to see what I could do to make that easier for future apps that might use this DAO (there will be a few). So I'll definitely keep this in mind when I get that far!

Thanks again for the information here. It was definitely helpful!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.