Testing with Spring

To improve the quality of your code it is important to thoroughly test your code. Not only using unit tests but also using integration tests. I’ll describe these terms and show examples on how to create tests using JUnit and Spring’s Test library.

Unit Testing

A unit-test is an automated test that tests one single unit-of-work. So, what is this unit-of-work? Its the smallest piece of code that can be distinguished. If you are programming in Java the smallest unit you can test is a single method. So all unit-tests should only test this single unit, and no other dependencies.

Here is an example of a unit-test:

public class CalculatorTest {

	private final Integer TOADD1 = 6;
	private final Integer TOADD2 = 4;
	private final Integer ANSWER = 10;
	
	@Test
	public void testAdd() {
		Calculator calculator = new Calculator();
		
		Integer returnedAnswer = calculator.add(TOADD1, TOADD2);
		
		Assert.assertEquals("The answer of " + TOADD1 + "+" + TOADD2 +
			" should be " + ANSWER + " but was " + returnedAnswer, 
			ANSWER, returnedAnswer);
	}
}

By adding the @Test annotation JUnit will know this is a unit test. When running the code we verify that it correctly adds two values.

Test Driven Development

Test Driven Development (TDD) is very important in modern software development. More and more people and businesses are using this technique to improve their code quality.

What TDD consists of, basically, is a very small development cycle for all your code. You just follow these steps:

  1. Write a unit-test that verifies your usecase
  2. Implement the interfaces you created for step 1
    (can be done quick and dirty, just to get the logic correct)
  3. Run the tests and confirm your code is logically correct
  4. Refactor your code into maintainable, readable, reusable code
  5. Confirm the code still works as designed by running the test(s)

Actually, I don’t write all my tests before the real code, but the process goes hand-in-hand. When writing code I always think about how I’m going to test it, and when the code is done, the test is too, combining step one and two. It speeds the process up a bit, and the code I end up with is almost the same because of the refactor-step.

Integration testing

With unit testing you can verify that the smallest units of work are correct. But this doesn’t give you any guarantees!

A famous example is NASA’s $125 million Mars Orbiter. It crashed when it was about to land on Mars. And why did it crash? Because one component calculated metric units and it feeded this information into another component that was using English units.

Very painfull for NASA, but a great example showing the importance of integration testing. Both NASA-components had been tested a lot, but apparently they neglected to test the components together.

Integration testing with Spring

Most business software in Java uses Spring. This is a framework glueing all your components together and providing dependency injection. This makes it easy to test all the classes and methods, but it makes it a bit harder to test the integration and communication between classes because you depend on the framework for that.

Luckely Spring helps us with a their Spring Test library full of tools that help you creating integration tests.

One thing you need in all integration tests is Springs Context. With Spring Test it is possible to wire some beans and let Spring inject those beans in your unit test. The old(-ish) way to do this is extending org.springframework.test.AbstractDependencyInjectionSpringContextTests or org.springframework.test.AbstractTransactionalSpringContextTests.

If you want to use these classes you need two things:

  • Create a seperate Spring configuration with test-beans (or use your normal Spring config)
  • Override String[] getConfigLocations(), provide it with the location of your Spring configuration.

Now, for all the private fields you have in your testclass Spring will attempt to auto-wire beans on it.

A small example:

public class PersonServiceTest 
	extends AbstractDependencyInjectionSpringContextTests {

	private PersonService personService;

	/**
	 * Integration test for my calculator.
	 * It also calls the calculatorDao which is in the calculatorService.
	 */
	@Test
	public void testAddPerson() {
		
		Person p = ...
		
		personService.add(p);
		
		//Test if the person has been added.
	}
	
	/**
	 * Provide the test with a configuration
	 */
	@Override
	protected String[] getConfigLocations() {
		return new String[] { "my-test-applicationcontext.xml" };
	}
}

Testing with Spring 2.5+

With the introduction of Spring 2.5 they improved the above method. For more flexability and a bit more control over the objects they introduced the SpringJUnit4ClassRunner.

This is how you would use it:

import org.junit.runner.RunWith;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= { "my-test-applicationcontext.xml" })
public class PersonServiceTest {

	@Autowired
	private PersonService personService;

	/**
	 * Integration test for my calculator.
	 * It also calls the calculatorDao which is in the calculatorService.
	 */
	@Test
	public void testAddPerson() {
		
		Person p = ...
		
		personService.add(p);
		
		//Test if the person has been added.
	}
}

It works the same way as before, but instead of having to override the AbstractDependencyInjectionSpringContextTests you now have a POJO. All the information about Spring and JUnit are placed in annotations on top of you class.

Hopefully I’ll find the time to write another post on mocking, including EasyMock and Mockito.