Saturday, January 12, 2013

Spring & Testing

I think unit testing Spring components is really cool. You can have a separate application context just for testing purposes, which will not conflict with your application's runtime context when developing.

Here is a couple of steps you need to take to test your components using Spring testing.

1. Add the following dependency to your pom file:
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>org.springframework.test</artifactId>
    <version>${spring.version}</version>
    <scope>test</scope>
</dependency>
where
    <spring.version>3.2.0.RELEASE</spring.version>

2. In your test/resources folder, your need to a create a package matching the your Test class, and create a context file with a name that matches your Test class name.

Here is an example:

Lets say you are testing a class called MessageProducer.
package com.noushin.spring.jms.producer;

import java.io.IOException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

@Component
public class MessageProducer {

   final static Logger logger = Logger.getLogger(MessageProducer.class);

   @Autowired
   private JmsTemplate jmsTemplate;

   public void produce() throws Exception {
      
      if (jmsTemplate != null) {
         MessageCreator mc = new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
               try {
                  String jmsMessage = FileUtils.readFileToString(FileUtils.toFile(this.getClass().getResource("/jms-message.txt")));
                  TextMessage message = session.createTextMessage(jmsMessage);
                  logger.info(">>>>>Sending message: " + jmsMessage);
                  return message;
               } 
               catch (IOException ioe) {
                  logger.error("File not found : ", ioe);
                  return null;
               }
               catch (JMSException je) {
                  logger.error("JMS Exception : ", je);
                  return null;
               }
            }
         };
         jmsTemplate.send(mc);
      }
   }
}

3. Here is your JUnit test case, ~workspace/jms/src/test/java/com/noushin/spring/jms/producer/MessageProducerTest.java
package com.noushin.spring.jms.producer;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MessageProducerTest {
   
   @Autowired
   protected MessageProducer producer;
   
   @Test
   public void testProduce() {
      try {
         producer.produce();
         assert(true);
      } 
      catch (Exception e) {
         e.printStackTrace();
      }
   }
}

4. Corresponding test context is at ~workspace/jms/src/test/resources/com/noushin/spring/jms/producer/MessageProducerTest-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />

    <!-- ActiveMQ destinations to use -->
    <amq:queue id="destination" physicalName="TestQ" />
    
    <!-- ActiveMQ broker URL -->
    <amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:61616" />

    <!-- Spring JMS ConnectionFactory -->
    <bean id="singleConnectionFactory" 
          class="org.springframework.jms.connection.SingleConnectionFactory"
          p:targetConnectionFactory-ref="jmsFactory"/>
    
    <!-- Spring JMS Producer Configuration -->
    <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
        p:connectionFactory-ref="singleConnectionFactory"
        p:defaultDestination-ref="destination"/>
        
</beans>

Alternatively, if there is a context configuration that can be reused by test classes in different packages, you can specify its location on classpath:
@ContextConfiguration(locations={"classpath:/com/noushin/spring/jms/producer/MessageProducerTest-context.xml"})

5. If in Eclipse, right click your test class and run it as JUnit Test. :)

No comments:

Post a Comment