Results 1 to 4 of 4

Thread: Best practices for Hibernate integration testing

  1. #1
    Join Date
    Feb 2005
    Posts
    11

    Default Best practices for Hibernate integration testing

    I am developing a web application using Spring and Hibernate and am looking for the best practices ways to test this app using Spring. I am enjoying the support offered through AbstractTransactionalDataSourceSpringContextTests for many of my tests. What I am cuurently doing for integration testing is writing test cases like the following:

    Code:
    public class AbstractUserIntegrationTests extends AbstractTransactionalDataSourceSpringContextTests {
    
    	protected BizFacade bizImpl;
    	
    	public void setBizFacade (BizFacade bizImpl) {
    		this.bizImpl= bizImpl;
    	}
    
    	public void testAddUser() {		
    		// Add user
    		User user = new User();
    		user.setUsername("btucker");
    		user.setPassword("tuckb989");
    		user.setScreenName("big tucker");
    		user.setEmailAddress("forget@about.it");		
    		this.bizImpl.addUser(user);		
    				
    		// Assert that user was added to database
    		List<User> users = this.bizImpl.findAllUsers();		
    		Assert.assertEquals("Incorrect number of users found", users.size(), 1);
    		
    		User loadedUser = users.get(0);
    		Assert.assertEquals("Username doesn't match", loadedUser.getUsername(), "btucker");		
    		Assert.assertEquals("Password doesn't match", loadedUser.getPassword(), "tuckb989");		
    		Assert.assertEquals("Screen name doesn't match", loadedUser.getScreenName(), "big tucker");		
    		Assert.assertEquals("Email address doesn't match", loadedUser.getEmailAddress(), "forget@about.it");				
    	}
    }
    This works great. But for linking multiple calls to Hibernate i have ran into trouble. Take for example testing the logic of deleting a user. Here is my first attempt:

    Code:
    	public void testDeleteUser() {
    		// Add user
    		User user = new User();
    		user.setUsername("btucker");
    		user.setPassword("tuckb989");
    		user.setScreenName("big tucker");
    		user.setEmailAddress("forget@about.it");		
    		this.bizImpl.addUser(user);		
            
    		// Assert that user was added to database
    		List<User> users = this.bizImpl.findAllUsers();		
    		Assert.assertEquals("Incorrect number of users found", users.size(), 1);        
    		
    		// Delete user
    		this.bizImpl.deleteUser(users.get(0));
    		
    		// Assert that user has been deleted from database
    		users = this.bizImpl.findAllUsers();	
    		Assert.assertEquals("Incorrect number of users found", users.size(), 0);	
    	}
    This leads to the following exception

    Code:
    org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
    If I change the deleteUser() call to from:
    Code:
    		this.bizImpl.deleteUser(users.get(0));
    to:
    Code:
    		this.bizImpl.deleteUser(user);
    the following error occurs.

    Code:
    org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [com.biz.domain.User#22]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.biz.domain.User#22]
    org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
    I understand from a previous post on this problem (http://forum.springframework.org/showthread.php?t=18793) that this happens because the object is already associated with the hibernate session.

    So my question is how are people doing this? How are Spring developers handling this scenerio?
    Last edited by robyn; May 14th, 2006 at 06:04 PM.

  2. #2
    Join Date
    Aug 2004
    Location
    San Mateo, CA
    Posts
    1,265

    Default

    Remember that you can clear the Hibernate session, removing objects already associated with it. This is often necessary before requerying in tests, and solves most (if not all) problems.

    I typically use JDBC for verification. The pattern is
    - do Hibernate operation
    - flush Hibernate session
    - issue JDBC query to verify results

    That way I'm verifying what Hibernate did to the database in the same transaction.
    Rod Johnson - GM, SpringSource Division, VMware
    http://www.springsource.com
    Spring From the Source

  3. #3
    Join Date
    Feb 2005
    Posts
    11

    Default Thanks for that insight...

    I was pleasantly surprised when I checked the forum and none other than Rod Johnson had posted the reply. Thanks for your response to this question and thanks for your huge contribution to the community through this framework and your books.

    For those who might be having similar problems I will post the changes I made to the testDeleteUser() code.

    Code:
    	public void testDeleteUser&#40;&#41; &#123;
    		// Add user
            long id = dbHelper.addUser&#40;"btucker", "little_one1", "big tucker", "forget@about.it"&#41;;
            
    		// Assert that user was added to database
    		List<User> users = this.bizImpl.findAllUsers&#40;&#41;;		
    		Assert.assertEquals&#40;"Incorrect number of users found", users.size&#40;&#41;, 1&#41;;        
    		
    		// Delete user
    		User user = new User&#40;&#41;;
    		user.setId&#40;id&#41;;
    		this.bizImpl.deleteUser&#40;user&#41;;
    		
    		// Assert that user has been deleted from database
    		users = this.bizImpl.findAllUsers&#40;&#41;;	
    		Assert.assertEquals&#40;"Incorrect number of users found", users.size&#40;&#41;, 0&#41;;	
    	&#125;
    Code:
    public abstract class AbstractTestDatabaseHelper &#123;
    
        private JdbcTemplate jdbcTemplate;
        
    	public void setJdbcTemplate&#40;JdbcTemplate jdbcTemplate&#41; &#123;
    		this.jdbcTemplate = jdbcTemplate;
    	&#125;
    
    	protected abstract String getIdentityQuery&#40;&#41;;
        
        public Long addUser&#40;String username, String password, String screenName, String emailAddress&#41; &#123;
            String sql = "insert into user &#40;username, password, screenname, emailaddress&#41; values &#40;?, ?, ?, ?&#41;";
            Object&#91;&#93; args = new Object&#91;&#93; &#123; username, password, screenName, emailAddress &#125;;
            jdbcTemplate.update&#40;sql, args&#41;;
            return jdbcTemplate.queryForLong&#40;getIdentityQuery&#40;&#41;&#41;;
        &#125;	
        
        public Long addTrackingDefinition&#40;long userID, String name&#41; &#123;
            String sql = "insert into tracking_definition &#40;user_id, name&#41; values &#40;?, ?&#41;";
            Object&#91;&#93; args = new Object&#91;&#93; &#123; userID, name &#125;;
            jdbcTemplate.update&#40;sql, args&#41;;
            return jdbcTemplate.queryForLong&#40;getIdentityQuery&#40;&#41;&#41;;
        &#125;    
    &#125;
    
    public class MySqlTestDatabaseHelper extends AbstractTestDatabaseHelper &#123;
    
    	protected String getIdentityQuery&#40;&#41; &#123;
    		return "select last_insert_id&#40;&#41;";
    	&#125;
    	
    &#125;

  4. #4
    Join Date
    Feb 2005
    Posts
    11

    Default

    As I flesh out the business logic for this application I am presented with another problem. I have put logic in place to handle adding users with duplicate usernames, screen names, or email addresses. I check this by adding the business logic to the domain facade similar to jpetstore's PetStoreImpl.

    But when I attempt to unit test (using AbstractTransactionalDataSourceSpringContextTests) Hibernate seems to not correctly handle the multiple calls to check for duplicates.

    The DuplicateUsernameException gets thrown when running the testUpdateUser().

    When debugging in Eclipse I have noticed that this call
    Code:
    User usernameUser = this.userDAO.findUserByUsername&#40;user.getUsername&#40;&#41;&#41;;
    returns the correct user but with an incorrect id. Hence the next check
    Code:
    		if &#40;usernameUser != null && usernameUser.getId&#40;&#41; != user.getId&#40;&#41;&#41;
    			throw new DuplicateUsernameException&#40;user.getUsername&#40;&#41;&#41;;
    throws the exception because it thinks there is already another user with this username.

    Any ideas? How are others testing there domain logic?

    Snip of Biz Facade
    Code:
    public class BizImpl implements BizFacade &#123;
    
    	private UserDAO userDAO;
    	
    	public void setUserDAO&#40;UserDAO userDAO&#41; &#123;
    		this.userDAO = userDAO;
    	&#125;
    
    	public User updateUser&#40;User user&#41; &#123;
    		// Check for duplicate username
    		User usernameUser = this.userDAO.findUserByUsername&#40;user.getUsername&#40;&#41;&#41;;
    		if &#40;usernameUser != null && usernameUser.getId&#40;&#41; != user.getId&#40;&#41;&#41;
    			throw new DuplicateUsernameException&#40;user.getUsername&#40;&#41;&#41;;	
    		
    		// Check for duplicate screen name
    		User screenNameUser = this.userDAO.findUserByScreenName&#40;user.getScreenName&#40;&#41;&#41;;
    		if &#40;screenNameUser != null && screenNameUser.getId&#40;&#41; != user.getId&#40;&#41;&#41;
    			throw new DuplicateScreenNameException&#40;user.getScreenName&#40;&#41;&#41;;	
    				
    		// Check for duplicate email address
    		User emailUser = this.userDAO.findUserByEmail&#40;user.getEmailAddress&#40;&#41;&#41;;
    		if &#40;emailUser != null && emailUser.getId&#40;&#41; != user.getId&#40;&#41;&#41;
    			throw new DuplicateEmailAddressException&#40;user.getEmailAddress&#40;&#41;&#41;;	
    		
    		return this.userDAO.update&#40;user&#41;;
    	&#125;	
    &#125;
    Snip of relevant test case
    Code:
    public class BaseUserIntegrationTest extends AbstractTransactionalDataSourceSpringContextTests &#123;
    	
    	protected BizFacade bizImpl;
    	protected AbstractTestDatabaseHelper dbHelper;
    	
    	public void setBizImpl&#40;BizFacade bizImpl&#41; &#123;
    		this.bizImpl = bizImpl;
    	&#125;	
    	public void setDbHelper&#40;AbstractTestDatabaseHelper dbHelper&#41; &#123;
    		this.dbHelper = dbHelper;
    	&#125;
    
        protected void onSetUpInTransaction&#40;&#41; throws Exception &#123;
            this.dbHelper.setJdbcTemplate&#40;this.jdbcTemplate&#41;;
        &#125;
    
        protected void onTearDownInTransaction&#40;&#41; throws Exception &#123;
            super.onTearDownInTransaction&#40;&#41;;
            clearCache&#40;&#41;;
        &#125;
    
        private void clearCache&#40;&#41; &#123;
        	SessionFactory sessionFactory = &#40;SessionFactory&#41;applicationContext.getBean&#40;"sessionFactory"&#41;;
    
            sessionFactory.evict&#40;User.class&#41;;
        &#125;
    
    	public void testUpdateUser&#40;&#41; &#123;
    		// Add user
            long userID = dbHelper.addUser&#40;"btucker", "little_one1", "big tucker", "forget@about.it"&#41;;
            		
    		// Update user with new username, password, and screen name
    		User userToUpdate = this.bizImpl.findUserByID&#40;userID&#41;;	
    		userToUpdate.setUsername&#40;"queen_bee"&#41;;
    		userToUpdate.setPassword&#40;"4509890"&#41;;
    		userToUpdate.setScreenName&#40;"yakami"&#41;;
    		userToUpdate.setEmailAddress&#40;"iamtheman@forget_about.it"&#41;;		
    		
    		this.bizImpl.updateUser&#40;userToUpdate&#41;;		
    
    		// Assert that user updated in database
    		User updatedUser = this.bizImpl.findUserByID&#40;userToUpdate.getId&#40;&#41;&#41;;				
    		
    		Assert.assertEquals&#40;"Username not updated", updatedUser.getUsername&#40;&#41;, "queen_bee"&#41;;
    		Assert.assertEquals&#40;"Password not updated", updatedUser.getPassword&#40;&#41;, "4509890"&#41;;
    		Assert.assertEquals&#40;"Screen name not updated", updatedUser.getScreenName&#40;&#41;, "yakami"&#41;;
    		Assert.assertEquals&#40;"Email address not updated", updatedUser.getEmailAddress&#40;&#41;, "iamtheman@forget_about.it"&#41;;
    	&#125;	
    &#125;
    Snip of Hibernate DAO
    Code:
    public class HibernateUserDAO extends GenericHibernateDAO<User, Long> implements UserDAO &#123;
    
    	public HibernateUserDAO&#40;&#41; &#123;
    		super&#40;User.class&#41;;
    	&#125;
    
    	public User findUserByUsername&#40;String username&#41; throws DataAccessException &#123;
            Query q = this.getSession&#40;&#41;.createQuery&#40;"from User u where u.username=&#58;username"&#41;;
            q.setParameter&#40;"username", username&#41;;
            return &#40;User&#41; q.uniqueResult&#40;&#41;;		
    	&#125;
    &#125;

Similar Threads

  1. Hibernate Integration testing problem
    By Ibexx in forum Data
    Replies: 4
    Last Post: Aug 19th, 2011, 01:41 PM
  2. Testing a View (integration tests)
    By hay7777 in forum Web
    Replies: 5
    Last Post: Jul 13th, 2005, 11:37 AM
  3. Please help! Unit testing code for JPetStore
    By lakershen in forum Container
    Replies: 4
    Last Post: Jan 13th, 2005, 05:00 PM
  4. Integration Testing with ContextTestCase
    By feester in forum Container
    Replies: 2
    Last Post: Aug 26th, 2004, 01:22 PM
  5. Replies: 2
    Last Post: Aug 26th, 2004, 08:55 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •