Announcement Announcement Module
Collapse
No announcement yet.
How to write Junit tests for Spring,Hibernate,JPA Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to write Junit tests for Spring,Hibernate,JPA

    we are following TDD(Test Driven Development) approach to develop projects.
    The new frameworks being used are Spring 2.5, Hibernate 3.2.x and Hibernate annotations(JPA). I wanted to write some test cases for the backend. Is there any step by step instruction which can get me going.

    I am searching through lot of places but I thought of asking to the community who can really help me and in a good way. Please suggest me what should I do? I am using Informix as the database and Ant and JBoss 5.0 as the server. Front end is being developed using struts 2.1.

    So please provide me link or any instructions where I can start developing the tests using JUNIT.

  • #2
    We don't run unit tests for the persistence layer, we run functional tests which actually connect to the database...to verify that all the HQL/SQL queries in our services are working correctly and that the services are populating all the fields in our model beans.

    It's pretty straight forward to configure your tests using the spring test runner. Typically for these kinds of tests I build a 'base' class that has all the services under test autowired into it.

    e.g.

    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring/myapp-db-beans.xml", "classpath:spring/myapp-db-test-beans.xml"})
    @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
    @Transactional
    public abstract class HibernateFunctionalTestBase
    {
        // Spring automatically will inject these at runtime
        
        @Autowired
        protected HibernateOrdersService ordersService;
        
        @Autowired
        protected HibernateDeviceService deviceService;
    
    ...
    I have a spring bean file that defines the db beans that are used in my application during an actual deployment (myapp-db-beans.xml) and then I override several settings in the spring bean file that is used in the tests (myapp-db-test-beans.xml). With spring, the last bean definition wins so if you have other collaborators (e.g. your hibernate session factory) that are wired to something you need to override in a test (e.g. your datasource) you can just redefine that bean in a later bean configuration file and the collaborators will be wired up with the last definition.

    For instance, in a test, I don't want to use the heavy weight transaction manager that is used in my deployed application (atomikos) so I override this with the hibernate transaction manager since my functional tests are only doing db work:

    myapp-db-test-beans.xml
    Code:
      <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
          <ref bean="myapp-db-hibernate-session-factory" />
        </property>
      </bean>
    I also override the pooled datasource that my deployed application uses with a driver based datasource for the tests:

    myapp-db-test-beans.xml
    Code:
     <bean name="myapp-db-datasource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${hibernate.connection.url}" />
        <property name="driverClassName" value="${hibernate.connection.driver_class}" />
        <property name="username" value="${hibernate.connection.username}" />
        <property name="password" value="${hibernate.connection.password}" />
      </bean>
    You can also override your hibernate properties as well if you want to tune the hibernate behavior in your tests differently than in your deployed apps (e.g. to show the sql).

    myapp-db-test-beans.xml
    Code:
        
    <bean id="myapp-db-hibernate-properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties">
          <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>       
            <prop key="hibernate.max_fetch_depth">3</prop>
            <!-- disable second lvl caching -->
            <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider
            </prop>
            <!-- Development settings -->
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.bytecode.use_reflection_optimizer">false</prop>
              <!--  
                Setting below is makes hibernate behaves properly with triggers.
              -->        
            <prop key="hibernate.jdbc.use_get_generated_keys">false</prop>        
          </props>
        </property>
      </bean>
    You can also put reusable methods in the base class (such as for creating test persistence objects and any associations that they need):

    Code:
    protected ApplicationServiceProvider createProvider()
        {
            ApplicationServiceProvider provider = new ApplicationServiceProvider();
            provider.setName("functional-test-asp");
            applicationServiceProviderService.create(provider);
            applicationServiceProviderService.flush();
            return provider;
        }
        
        protected Carrier createCarrier()
        {
            Carrier carrier = new Carrier();
            carrier.setName("functional-test-carrier");
            
            carrierService.create(carrier);
            carrierService.flush();
            return carrier;
        }
        
        protected Application createApplication(ApplicationServiceProvider provider, Carrier carrier)
        {
            Application application = new Application();
            application.setName("functional-test-application");
            application.setApplicationServiceProvider(provider);
            application.setCarrier(carrier);
            applicationService.create(application);
            applicationService.flush();
            return application;
        }
    Then your service test just extends the test base:

    Code:
    public class HibernateApplicationServiceTest extends HibernateFunctionalTestBase
    ...
    It's all pretty straightforward...

    Comment


    • #3
      To test the USERS of your service/persistence layer, you should use something like JMock. Jmock integrates very well with Junit 4.x via the @RunWith annotation.

      e.g.
      Code:
      @RunWith(JMock.class)
      MyTest
      {
      ...
      }
      You can mock out all your service calls when you are testing the behavior of the classes that are collaborating with your service layer--you know that your service layer works because you've tested it in the functional tests above

      Comment


      • #4
        Thank you very much for your quick reply. I am gonna try it pretty soon today.

        Is there any additional jar file I need to include in my class path for this?

        Also I am using DAO and DAO impl classes and I am using Resource_Local in the persistence.xml along with the entity classes.

        I really dont know if persistence.xml needs to be in the picture at all or not?

        So you mean to say(correct me if I am wrong) I can extend those base classes in my DAOImpl classes and I should be good to go?

        For instance: CustomerDAO.java

        public interface CustomerDAO {
        public void save(CustomerEntity entity);
        }

        @Transactional
        public class CustomerDAOImpl implements CustomerDAO{
        public void save(CustomerEntity entity) {

        ------
        -----
        }
        }


        Please guide me through this what should I do. I know I might be asking stupid questions but I am just starting to use spring framework. If I am able to do it once I really wont bother anybody.... Thanks again for your post.

        Comment


        • #5
          Yes, in fact you can implement much of your DAO's with generics. This is what we did...

          All the stuff below you could replace Service with Dao and it would be basically the same...

          Code:
          public interface BaseService<T>
          {
              /**
               * Find all of the objects of the given type.
               * 
               * @param type the model type to find
               * @return a collection of all instances of the given type
               */
              public List<T> findAll(Class<T> type);
              
              /**
               * Find the model with the given id(PK).
               * 
               * @param type the model type
               * @param id the pk
               * @return the model with the given id (PK) or null if no such model was found
               */
              public T findByPK(Class<T> type, Serializable id);
              
              /**
               * Create (save) the persistent model.
               * 
               * @param modelObject the model to create
               */
          	public void create(T modelObject);
          	
          	/**
          	 * Update the persistent state to reflect the model.
          	 * 
          	 * @param modelObject the model to update
          	 */
          	public void update(T modelObject);
          	
          	/**
          	 * Delete the persistent state for the model.
          	 * 
          	 * @param modelObject the model to delete
          	 */
          	public void delete(T modelObject);
          }
          Then the base implementation (for hibernate):
          Code:
          public abstract class HibernateBaseService<T> implements BaseService<T>
          {    
          	protected SessionFactory sessionFactory;
          	
          	public void flush()
          	{
          	    this.sessionFactory.getCurrentSession().flush();
          	}
          	
          	protected Session getSession()
              {
                  return this.sessionFactory.getCurrentSession();
              }
          	
              @Autowired(required=true)	
              public void setSessionFactory(SessionFactory sessionFactory) 
              {
                  this.sessionFactory = sessionFactory;
              }
          
              
              @SuppressWarnings("unchecked")
              @Override
              public List<T> findAll(Class<T> type)
              {
                  String queryString = "from " + type.getCanonicalName() + " model";
                  return Collections.checkedList(getSession().createQuery(queryString).list(), type);
              }
          
          ...
          }
          Then your service interface:
          Code:
          public interface CustomerService extends BaseService<Customer>
          {
             // only add additional methods if you need more than the base provided in the interface
          }
          Then your service impl:

          Code:
          public class HibernateCustomerService extends HibernateBaseService<Customer> implements CustomerService
          {
          // don't add any additional methods unless required by the customer service interface
          }

          Comment

          Working...
          X