I like software - reading, tinkering, designing, coding. I have been doing so for 20 years or so and I would not mind continuing this for foreseeable future. Fortunately for me, this is my profession as well and I have managed to get paid for this for some 14 years now. Although I do not have any strong bias for any business domain, I have been working with some pretty big names in the finance domain and you might get a hint of that from my entries. Partha is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

JUnit, Logback, Maven with Spring 3

08.01.2012
| 8808 views |
  • submit to reddit

In this series we have already learnt to set up a basic Spring MVC application and learnt how to handle forms in Spring MVC. Now it is time to take on some more involved topics. However, before we venture into deeper waters, let's get some basics set up.

Unit testing
I am no TDD evangelist. There I said it. I have never ever been able to write any software where for every piece of code, I have written a test first and then code. If you have done so and are gainfully employed by coding, please do let me know. I would seriously like to know you better. Seriously.

My difference in opinion with TDD ends there. Apart from writing test before code - which somehow I simply can't get my brain to work with - I am a huge supporter of unit testing. I am a firm believer of using JUnit to test all functionality (public but non getter setter, methods). I am a huge fan of using cobertura to report on code coverage. I am a huge fan of maven which allows me to bring this all together in a nice HTML report with just one command.

I will use JUnit 4 for this series. Let's add the dependencies.

File: \pom.xml

<properties>                                                     
    <junit.version>4.10</junit.version>
</properties>  

<!-- Unit testing framework. -->       
<dependency>                           
    <groupId>junit</groupId>           
    <artifactId>junit</artifactId>     
    <version>${junit.version}</version>
    <scope>test</scope>                
</dependency>                          

And let's add a dumb class to demonstrate testing.

File: /src/main/java/org/academy/HelloWorld.java 

package org.academy;

public class HelloWorld {
  private String message = "Hello world. Default setting."; 
  public String greet(){
    return message; 
  }
  
  public String getMessage() {
    return message;
  }
  public void setMessage(String message) {
    this.message = message;
  }
}

And finally the JUnit to test it.

File: src/test/java/org/academy/HelloWorldTest.java 

package org.academy;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 HelloWorldTest {

  @Autowired
  HelloWorld helloWorld;
  
  private final static Logger logger = LoggerFactory
      .getLogger(HelloWorldTest.class);

  @Test
  public void test() {    
    logger.debug(helloWorld.greet());
    assertEquals(helloWorld.greet(), "Hello world, from Spring.");
  }
}

You would have noticed that the helloWorld within the unit test have never been initialized in the code. This is the bit of IoC magic of Spring. To make this work, we have used @RunWith, @ContextConfiguration and @Autowired. And I have also given Spring enough information to be able to create an instance of HelloWorld and then inject it to HelloWorldTest.helloWorld. Also, the assertEquals is checking for a very different message than what is actually hard coded in the HelloWorld class. This was done in a xml file mentioned below. Please do note the location of the file within Maven structure.

File: /src/test/resources/org/academy/HelloWorldTest-context.xml 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  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">

  <bean id="helloWorld" class="org.academy.HelloWorld">
    <property name="message" value="Hello world, from Spring." />
  </bean>
</beans>

There are multiple ways I could have provided this configuration file to the unit test. @RunWith(SpringJUnit4ClassRunner.class) is a nice thing to add but is not mandatory. What I have provided here is just the vanilla approach that works in most cases, but I encourage the audience to experiment.

Unit test coverage / code coverage.
I don't feel there is enough said about the importance of automated / semi automated / easy way of reporting on code coverage - both for individual developers and technical heads. Unless you are practising TDD religiously (which by the way I have mentioned before I personally have never been able to), it is absolutely impossible for even an individual developer to know if all logic branches of a code are covered by unit test. I am not even going to talk about how a technical head of a team / organization is going to ensure that his product(s) are sufficiently unit tested. I personally believe, any software product which is not sufficiently unit tested and test coverage reported, is an unacceptable risk. Period. Admittedly a bit of a hard stance, but that's how it is.

A bit of my conviction for the hard stance comes from the fact that it is so darn easy to report on test coverage. I will use cobertura in this example. You need to add cobertua to Maven pom.

File: pom.xml 

<!-- Reporting -->                                              
<plugin>                                                              
  <groupId>org.apache.maven.plugins</groupId>                       
  <artifactId>maven-site-plugin</artifactId>                        
  <version>3.0</version>                                            
  <configuration>                                                   
    <reportPlugins>                                               
      <!-- Reporting on success / failure of unit tests -->     
      <plugin>                                                  
        <groupId>org.apache.maven.plugins</groupId>           
        <artifactId>maven-surefire-report-plugin</artifactId> 
        <version>2.6</version>                                
      </plugin>                                                 
      <!-- Reporting on code coverage by unit tests. -->        
      <plugin>                                                  
        <groupId>org.codehaus.mojo</groupId>                  
        <artifactId>cobertura-maven-plugin</artifactId>       
        <version>2.5.1</version>                              
        <configuration>                                       
          <formats>                                         
            <format>xml</format>                          
            <format>html</format>                         
          </formats>                                        
        </configuration>                                      
      </plugin>                                                 
    </reportPlugins>                                              
  </configuration>      

And once you have done this, and added JUnit, and added an actual JUnit test, you just need to run

    mvn -e clean install site  

to create a nice looking HTML based code coverage report. This report will allow you to click through source code under test and give you nice green coloured patches for unit tested code and red coloured patches for those that slipped through the cracks.

Logging
Log4j is good, Logback is better. Just don't use System.out.println() for logging.

You could go a long way without proper logging. However, I have spent far too many weekends and nights chasing down production issues, with business breathing down my neck, wishing there was some way to know what was happening in the app rather than having to guess all my way. Now a days, with mature api like slf4j and stable implementation like logback, a developer needs to add just one extra line per class to take advantage of enterprise grade logging infrastructure. It just does not make sense not to use proper logging right from the beginning of any project.

Add slf4j and logback to Maven dependencies.

File: \pom.xml.

<!-- Logging -->                            
<dependency>                                
  <groupId>ch.qos.logback</groupId>       
  <artifactId>logback-classic</artifactId>
  <version>${logback.version}</version>   
</dependency>                               

Ensure that Spring's default logging i.e. commons logging is excluded. If you are wondering if logback is really this good that I claim it to be why did Spring not opt for it to start with. In my defense, here is a link at Spring's official blog where they say "If we could turn back the clock and start Spring now as a new project it would use a different logging dependency. Probably the first choice would be the Simple Logging Facade for Java (SLF4J),..."

File: \pom.xml.

                                                     
<!-- Support for testing Spring applications with too
  TestNG This artifact is generally always defined 
  the integration testing framework and unit testin
<dependency>                                         
  <groupId>org.springframework</groupId>           
  <artifactId>spring-test</artifactId>             
  <version>${org.springframework.version}</version>
  <scope>test</scope>                              
  <exclusions>                                     
    <exclusion>                                  
      <groupId>commons-logging</groupId>       
      <artifactId>commons-logging</artifactId> 
    </exclusion>                                 
  </exclusions>                                    
</dependency>                                        

Provide configuration for logback.

File: /src/main/resources/logback.xml

                                                    
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d %5p | %t | %-55logger{55} | %m %n</pattern>
    </encoder>
  </appender>

  <logger name="org.springframework">
    <level value="INFO" />
  </logger>

  <root>
    <level value="DEBUG" />
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>
                                     

Finally, add the magic one liner at the beginning of each class that needs logging (that ought to be all classes).

File: src/test/java/org/academy/HelloWorldTest.java

[...]                                                    
private final static Logger logger = LoggerFactory  
  .getLogger(HelloWorldTest.class);           
[...]
logger.debug(helloWorld.greet());
[...]

There you are all set up. Now is the time to wade deeper into Spring.

Happy coding.

Published at DZone with permission of Partha Bhattacharjee, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)