Enterprise Java

Spring MVC: Introduction in testing

Testing is one of the most important parts of software development. Well organized testing helps to keep a code of application in a good state, in a working state. There are a lot of different types of test and methodologies. In this article I want to make an introduction in unit testing of applications based on Spring MVC. Don’t hope to read all about Spring MVC testing here, because it’s just a first article about unit testing.

Speak about unit testing without some application, which I’m going to test is deception. I’ll use one of my applications from the previous post, to avoid a gossip. I
 
recommend to make a short overview of the application before you proceed read current post. The main goal of this tutorial is to demonstrate how to configure unit tests for a Spring MVC application in annotation maner.

Preparations

The first thing which we always have to do before start any development – add of new dependencies in Maven’s pom.xml file. This case isn’t an exception.

...
		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-test</artifactid>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-test-mvc</artifactid>
			<version>1.0.0.M1</version>
			<scope>test</scope>
		</dependency>
...
	<repositories>
		<repository>
			<id>spring-maven-milestone</id>
			<name>Spring Maven Milestone Repository</name>
			<url>http://maven.springframework.org/milestone</url>
		</repository>
	</repositories>
...

I have added two new dependencies:

  1. #1 spring-test
  2. #2 spring-test-mvc

The first one is for support for testing Spring applications with tools such as JUnit and TestNG. This artifact is generally always defined with a ‘test’ scope for the integration testing framework and unit testing stubs. The second one is for testing Spring MVC server-side and client-side RestTemplate-based code. Pay your attention that I added a new repository. I did this because spring-test-mvc still isn’t in official maven repository.

Controller for the unit tests

In this post I’m going to write two unit tests for the most simple controller. Here is a code of the controller:

@Controller  
    public class LinkController {  

        @RequestMapping(value="/")  
        public ModelAndView mainPage() {  
            return new ModelAndView("home");  
        }  

        @RequestMapping(value="/index")  
        public ModelAndView indexPage() {  
            return new ModelAndView("home");  
        }  

    }

So as you can see the methods in the controller is trivial, they just return some JSP. The testing of the controller implies check of request status (in success case the code should be 200) and verification of the view’s name.

Writing of unit tests for Spring MVC

Here is a quote of Petri Kainulainen:

The heart of the spring-test-mvc is a class called MockMvc that can be used to write tests for any application implemented by using Spring MVC. Our goal is to create a new MockMvc object by using the implementations of the MockMvcBuilder interface. The MockMvcBuilders class has four static methods which we can use to obtain an implementation of the MockMvcBuilder interface. These methods are described in following:

  • ContextMockMvcBuilder annotationConfigSetup(Class… configClasses) method must be used when we are using Java configuration for configuring the application context of our application.
  • ContextMockMvcBuilder xmlConfigSetup(String… configLocations) must be used when the application context of our application is configured by using XML configuration files.
  • StandaloneMockMvcBuilder standaloneSetup(Object… controllers) must be used when we want to configure the tested controller and the required MVC components manually.
  • InitializedContextMockMvcBuilder webApplicationContextSetup(WebApplicationContext context) must be used when we have already created a fully initialized WebApplicationContext object.

I’m going to use the Web Application Context, for this purpose I need to create a class with configurations:

package com.sprhib.init;

import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.*;

@Configuration
@ComponentScan("com.sprhib")
@EnableWebMvc
public class BaseTestConfig {

	@Bean
	public UrlBasedViewResolver setupViewResolver() {
		UrlBasedViewResolver resolver = new UrlBasedViewResolver();
		resolver.setPrefix("/WEB-INF/pages/");
		resolver.setSuffix(".jsp");
		resolver.setViewClass(JstlView.class);
		return resolver;
	}

}

And finally the class with the tests:

package com.sprhib.test;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.Before;
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;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.sprhib.init.BaseTestConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes=BaseTestConfig.class)
public class LinkControllerTest {

	@Autowired
	private WebApplicationContext wac;

	private MockMvc mockMvc;

	@Before
	public void init() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	@Test
	public void testHomePage() throws Exception {
		mockMvc.perform(get("/"))
			.andExpect(status().isOk())
			.andExpect(view().name("home"));
	}

	@Test
	public void testIndexPage() throws Exception {
		mockMvc.perform(get("/index.html"))
			.andExpect(status().isOk())
			.andExpect(view().name("home"));
	}

}

Notice that I have used static imports they provide usage of such methods as get(), status() etc. @WebAppConfiguration is a class-level annotation that is used to declare that the ApplicationContext loaded for an integration test should be a WebApplicationContext. Look at the project structure after I have added all testing stuff:

Check the project on GitHub. I hope everything is clear. Spring test MVC project is a good tool for the testing of the appropriate applications. There’s just one cons lack of documentation and tutorials. In the next tutorials I’m going to develop this theme.
 

Reference: Spring MVC: Introduction in testing from our JCG partner Alexey Zvolinskiy at the Fruzenshtein’s notes blog.

Alexey Zvolinskiy

Alexey is a test developer with solid experience in automation of web-applications using Java, TestNG and Selenium. He is so much into QA that even after work he provides training courses for junior QA engineers.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
SpringByExample UA
10 years ago

Nice article Alexey. I have a few things to add. First of all I do not understand why do you need both spring-test and spring-test-mvc? This is only needed when you have older spring versions and this should be mentioned explicitly. There is a list of things which you CAN and which you cannot do with standalone context – otherwise Rossen would give us only one opportunity. And finally it will be good to have some examples how to mock a service layer in a controllers.

Alex Fruzenshtein
Alex Fruzenshtein
10 years ago

Thanks for the comment

1. As specified in the documentation, spring-test is required for
testing Spring applications with tools such as JUnit and TestNG. Even
more, I use static imports in the LinkControllerTest, they are from the
spring-test dependency.

2. Regarding testing of a service layer, I will write some articles about it soon.

SpringByExample UA
10 years ago

I did not mean the service layer tests. The controller usually depends on some of your services. How in this case you plan to deal with that? That would make an example full.

FruzNotes
10 years ago

Usually yes, almost all controllers depend on some service. But in my case I just made the introduction in testing. In the following articles I will extend the application, new tests will use some mock frameworks

Agung Setiawan
10 years ago

i’m ready for the next series :D

Daniel Gagnon
Daniel Gagnon
5 years ago

I tried to import your project, but it fails at running the test. The only difference between your application on GIT and mine is that I’m using an Oracle 11G XE database instead of MySQL. It shouldn’t be a problem … GRAVE: Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@4eb386df] to prepare test instance [com.sprhib.test.LinkControllerTest@79517588] java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99) at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:122) at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:105) at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:74) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:312) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)… Read more »

Back to top button