Posted by: Ivko | October 28, 2010

Unit testing services, part 1 – Spring

Introduction

In today’s world the software industry is going towards continuous integration and test driven development. This brings requirement of testing each piece of code created in an organization and running this as often as possible. At the same time the applications are getting much more complex, relying on different containers to provide their collaborator objects and to care about their life cycle. Unit testing such managed objects becomes very hard and this demotivates even the toughest developers to write and maintain good unit tests. No matter how you call these special classes – services, session beans, data access objects, etc., the problem that needs to be solved is how you get hold of the infrastructure and the various collaborators that are usually provided by the container.

Of course you can always create complicated framework where you can run complex scenarios testing your service code. It’s a good thing to have such integration tests that run every night. But it would be great if during development (virtually on each save) you are able to test your code in as much isolation as possible. Which means to create tests closer to the unit test paradigm.

In this series of blog posts I will try to show you how you can use the test infrastructures provided by Spring framework, Embedded Glassfish and Arquillian in order to easily test complex managed objects like services, EJBs, DAOs, etc.

Let’s first start with our…

Usecase

Suppose you want to build a football statistics application. You want to keep track of all sorts of stuff around the matches played on our planet. One of the most important parts of your domain model is the team. So you want to know what is team’s name, which is its city, what is the name of its stadium and of course which is the country where it competes. In order to keep everything as simple as possible, let’s stop with the domain model right here. As a first step maybe we would like to provide a service that adds a new team to our database and also retrieves from there all the teams from a particular country.

I assume that you have basic knowledge about JPA as it will be the technology that I will use for describing and persisting the domain model. I also hope that you know Maven, because I will use that to setup, build and test our services from the command line. You can download the latest version of Maven from here.

I will use Eclipse 3.6 as development environment throughout the series. It can be downloaded from here. It doesn’t matter which of the packages you will choose. The example is simple enough and you can run it even with the most basic functionality. You will also need the m2eclipse plugin.

Setting the things up

In order to start, you need a project setup. You can both create the directory structure manually or use one of maven’s archetypes to do it for you. Let’s go to the second approach. Just run from the command line:

mvn archetype:create -DgroupId=com.foo -DartifactId=my-bar-spring -DarchetypeArtifactId=maven-archetype-webapp

This will create a default maven directory structure with pom.xml inside. The latter has to be tweaked a little bit. First change the compiler version to 1.6 (by default maven compiles to 1.4). As we are going to create JUnit4 unit tests, we’ll change the JUnit version in pom.xml as well:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <inherited>true</inherited>
      <configuration>
        <source>1.6</source>
        <target>1.6</target>
      </configuration>
    </plugin>
  </plugins>
</build>

Now it’s time to import our project in Eclipse and start adding the source code.

First, let’s create with our domain class – Team. Under src/main create a directory called java and then create our sample package (com.foo). After that create the Team class with the following attributes:

@Entity
@NamedQuery(name = "findAllTeamsByCountry", query = "select t from Team t where t.country = :country")
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private String city;
    private String stadium;
    private String country;
...
// Getters and setters skipped for clarity
}

As you can see it is an annotated POJO that will result in the DB table called TEAM, with one column for each of the attributes and also with primary key column called ID, where the values will automatically be generated. We have also defined one named query, which will be used to return all the teams in a championship. I have omitted the getters and setters, which you can leave to your ID to create for you.

Of course in order for this to compile you will need to enter the following dependency in your pom.xml:

<dependency>
  <groupId>javax.persistence</groupId>
  <artifactId>persistence-api</artifactId>
  <version>1.0</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>

Creating the Spring service

The code above will be exactly the same in this and the next two posts. However, when it comes to the reusable service, Spring and pure Java EE have slightly different approaches. What you call a session EJB in Java EE is known as component in Spring framework. The component as the session bean is annotated with one of the available annotations, but is not transactional by default.

In Spring, if you want to create reusable service, then the annotation that you will usually use for decorating your component is @Service. In order to get hold of all the Spring framework specific libraries for your service code to compile, add this to your pom.xml:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.0.4.RELEASE</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>3.0.4.RELEASE</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>

And here is the code of our Spring-driven team service:

@Service
public class TeamService {

    @PersistenceContext(unitName = "barPU")
    private EntityManager em;

    @Transactional
    public Team createTeam(Team team) {
        em.persist(team);
        return team;
    }

    @SuppressWarnings("unchecked")
    public List<Team> findAllTeamsFromCountry(String country) {
        Query query = em.createNamedQuery("findAllTeamsByCountry");
        query.setParameter("country", country);
        return query.getResultList();
    }
}

First we tell the Spring container that we want that class to be treated as a service and thus to be injectable in other code managed by that container. This is achieved through the @Service annotation. You can see that our service does not necessary implement an interface. However, it is good practice to work against interfaces and not with concrete implementations. As the service will perform basic data access activities, it would need a reference to the JPA entity manager to help it with that. We expect that the Spring container would manage it for us and inject it in our service. This is declared in Java EE standard way – through the @PersistenceContext annotation. The annotation itself is configured to look up the barPU persistence unit from the persistence.xml.

Creating a team should be done inside a transaction. As Spring beans are not transactional by default, the method is decorated with @Transactional. It is also good practice to make getter method transactional as well (even with read-only transactions), but I have skipped that in this example. In order to run the method in transactional context, Spring framework applies AOP techniques. If our team service implemented an interface, Spring would use the dynamic proxy mechanism from the JDK. However, here we need the Code Generation Library (a.k.a. cglib) for the proxying to work. So we add it in our pom.xml’s dependencies:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

The rest of the service code is purely calling the entity manager interface to persist or query our objects to/from the database.

Wiring things up

Now we have to make sure that everything that we created so far is glued together. This is assured by two basic artifacts:

  • JPA persistence context configuration
  • Spring container configuration

JPA persistence context is configured in the persistence.xml. Its default location is under the META-INF directory of the jars we create, which in terms of Maven directory layout is under src/main/resources/META-INF. Here is our persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="barPU">
        <class>com.foo.Team</class>
    </persistence-unit>
</persistence>

The only things that we declare here are the persistence unit name and the list of entities managed by this persistence unit. You noticed that the persistence unit name maps exactly to the name declared in the entity manager declaration’s annotation in our team service. Maybe you also saw that we have not declared here any database connectivity settings. We leave that to the Spring’s application context configuration. And here it comes:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx" 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-3.0.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.foo" />
    <context:property-placeholder location="/META-INF/spring/jdbc.properties"/>

    <bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="jpaVendorAdapter"
      class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="database" value="${database.type}"/>
        <property name="showSql" value="true"/>
    </bean>

    <bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="barPU"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

Here is a brief description of the above instructions to the spring container:

  • <context:annotation-config/> enables the injection of beans inside other beans just with annotating them with @Autowired. We’ll use them later in our unit test
  • <context:component-scan base-package=”com.foo”/> enables the discovery of all the beans under the com.foo package, that are annotated with @Component, @Service, @Repository or any other such annotation
  • <context:property-placeholder location=”/META-INF/spring/jdbc.properties”/> tells the container that this properties file contains some substituti0n values for macros defined in this application context (and not only)
  • The dataSource bean defines a Spring framework data source helper. It is initialized with DB driver, URL, username and password, which values are taken from the file declared in the above entry
  • The jpaVendorAdapter bean contains vendor-specific JPA settings
  • The entityManagerFactory bean is another Spring wrapper, this time around the JPA entity manager factory
  • The transactionManager bean declares Spring’s implementation used by the container to manage transactions whenever is needed
  • <tx:annotation-driven transaction-manager=”transactionManager”/> enables annotating transactional methods and classes (remember our createTeam method?)

And here is the jdbc.properties file, which was configured in the application context definition:

database.type=HSQL
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:mem:foobar
jdbc.username=sa
jdbc.password=

As you can see, these database connectivity settings do not fit a production setup (using non-persistent database with default user and password). This is so, because the above configuration will only be used for our tests. That is why the above properties file is placed under the src/test/resources directory of our project.

Writing the test

Finally we reached our goal. We’ll start writing the test. I guess that the numerous TDD practitioners and evangelists would place this section in front of all the rest, but I decided to leave writing the test as a dessert.

So it will be placed in the com.foo package under the src/test/java project directory. Here is the code:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/applicatonContext.xml" })
public class TeamServiceTest {

    @Autowired
    private TeamService teamService;

    @Test
    public void testTeamService() {
        Team testTeam = new Team();
        testTeam.setName("CSKA");
        testTeam.setCity("Sofia");
        testTeam.setCountry("Bulgaria");
        testTeam.setStadium("Bulgarska armia");

        teamService.createTeam(testTeam);

        assertEquals(1, teamService.findAllTeamsFromCountry("Bulgaria").size());
    }
}

It is a pure JUnit 4 unit test with some Spring framework spice in it:

  • JUnit will run this using SpringJUnit4ClassRunner
  • The Spring test context framework is told that the Spring application context is located under /META-INF/spring/applicationContext.xml in the application’s classpath
  • The class under test (TeamService) is injected by the Spring container

With the help of the highlighted lines we tell Spring framework to load the application context for us, initialize all the beans that we need and inject them inside our test. Thus we are able to proceed directly to testing our team service without bothering too much about stubbing or mocking the entity manager, which is not the most pleasant task.

The above class is actually more an integration than a pure unit test. It takes some time to load the Spring container. But this is done only once per test suite. So if you create several such test cases with numerous tests each, you will pay this penalty just once.

One final step is to add certain dependencies to pom.xml:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>3.0.4.RELEASE</version>
  <type>jar</type>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <version>2.0.0</version>
  <type>jar</type>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>3.4.0.GA</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.6.1</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>3.0.4.RELEASE</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>3.0.4.RELEASE</version>
  <type>jar</type>
  <scope>runtime</scope>
</dependency>

The test should work fine when you run it both from Eclipse and from the command line with Maven.

Conclusion

In this first part of the series we were able to create step by step an entity and a very simple data access object using Spring and JPA. Finally we created a lightweight integration test with the help of the Spring test context framework. It very much resembled a unit test as all the work for establishing the test environment and creating the collaborators of the class under test was left to the Spring container.

In the next part of the series you will see how to do the same thing but with Java EE session beans and the embedded Glassfish server.

Resources

You can download the source code of the above sample from here


Responses

  1. Excellent technical article. Knoledge packed. Short and Sweet.

  2. Thanks for your comment!

    The fact is that the Spring way of unit/integration testing is well documented and it was very easy for me to set it up and execute it.

    Now, to implement the examples in the next two parts was a challenge. So, follow this series if you are interested in Java EE and testing.

  3. If some one needs expert view concerning blogging then
    i propose him/her to go to see this blog, Keep up the fastidious job.

  4. I never thought I’d be writing about a farmacia on line IP case in 2012. Putting aside all the philosophical ideas about ‘no man is an island’ why should I have to admit that we’re more than a cost-saving
    move.

  5. Advantage paphos car hires assure complete privacy protection regarding information provided while booking such as including credit card details, phone number, agency, before becoming the city by car, Ms.

  6. Thanks a lot for the helpful post. It is also my opinion that mesothelioma cancer has an particularly long
    latency phase, which means that warning signs of
    the disease may not emerge right until 30 to 50
    years after the 1st exposure to asbestos fiber.
    Pleural mesothelioma, that is certainly the
    most common kind and has effects on the area across the lungs,
    might result in shortness of breath, breasts pains,
    along with a persistent cough, which may result in coughing up
    blood.

  7. Me ha impresionado este articulo, en realidad es que está muy cuidada la forma de escribir, lo que permite disfrutar de la lectura.
    Un día leí una cosa que trataba de esto, y la verdad es que tambien me fascinó.
    Espero que prosigas posteando artículos tan impresionantes como este, ya
    que voy a visitar frecuentemente tu blog.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: