Writing unit tests for Spring MVC controllers has traditionally been both simple and problematic.
Although it is pretty simple to write unit tests which invoke controller methods, the problem is that those unit tests are not comprehensive enough.
For example, we cannot test controller mappings, validation and exception handling just by invoking the tested controller method.
Spring MVC Test solved this problem by giving us the possibility to invoke controller methods through the DispatcherServlet.
This is the first part of my tutorial which describes the unit testing of Spring MVC controllers and it describes how we can configure our unit tests.
Let's get started.
Getting the Required Dependencies with Maven
We can get the required dependencies by declaring the following testing dependencies in our pom.xml file:
- JUnit 4.11
- Mockito Core 1.9.5
- Spring Test 3.2.3.RELEASE
The relevant part of our pom.xml file looks as follows:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.2.3.RELEASE</version> <scope>test</scope> </dependency>
Note: If you have to use Spring Framework 3.1, you can write unit tests for your controllers by using spring-test-mvc. This project was included in the spring-test module when Spring Framework 3.2 was released.
Let's move on and take a quick look at our example application.
The Anatomy of Our Example Application
The example application of this tutorial provides CRUD operations for todo entries. In order to understand the configuration of our test class, we must have some knowledge about the tested controller class.
At this point, we need to know the answers to these questions:
- What dependencies does it have?
- How is it instantiated?
We can get the answers to those questions by taking a look at the source code of the TodoController class. The relevant part of the TodoController class looks as follows:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.stereotype.Controller; @Controller public class TodoController { private final TodoService service; private final MessageSource messageSource; @Autowired public TodoController(MessageSource messageSource, TodoService service) { this.messageSource = messageSource; this.service = service; } //Other methods are omitted. }
As we can see, our controller class has two dependencies: TodoService and MessageSource. Also, we can see that our controller class uses constructor injection.
At this point this is all there information we need. Next we will talk about our application context configuration.
Configuring the Application Context
Maintaining a separate application context configurations for our application and our tests is cumbersome. Also, It can lead into problems if we change something in the application context configuration of our application but forget to do the same change for our test context.
That is why the application context configuration of the example application has been divided in a such way that we can reuse parts of it in our tests.
Our application context configuration has been divided as follows:
- The first application configuration class is called ExampleApplicationContext and it is the "main" configuration class of our application.
- The second configuration class is responsible of configuring the web layer of our application. The name of this class is WebAppContext and it is the configuration class which we will use in our tests.
- The third configuration class is called PersistenceContext and it contains the persistence configuration of our application.
Note: The example application has also a working application context configuration which uses XML configuration files. The XML configuration files which correspond with the Java configuration classes are: exampleApplicationContext.xml, exampleApplicationContext-web.xml and exampleApplicationContext-persistence.xml.
Let's take a look at the application context configuration of our web layer and find out how we can configure our test context.
Configuring the Web Layer
The application context configuration of the web layer has the following responsibilities:
- It enables the annotation driven Spring MVC.
- It configures the location of static resources such as CSS files and Javascript files.
- It ensures that the static resources are served by the container's default servlet.
- It ensures that the controller classes are found during component scan.
- It configures the ExceptionResolver bean.
- It configures the ViewResolver bean.
Let's move on and take a look at the Java configuration class and the XML configuration file.
Java Configuration
If we use Java configuration, the source code of the WebAppContext class looks as follows:
mport org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; import java.util.Properties; @Configuration @EnableWebMvc @ComponentScan(basePackages = { "net.petrikainulainen.spring.testmvc.common.controller", "net.petrikainulainen.spring.testmvc.todo.controller" }) public class WebAppContext extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("/static/"); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Bean public SimpleMappingExceptionResolver exceptionResolver() { SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException", "error/404"); exceptionMappings.put("java.lang.Exception", "error/error"); exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404"); statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver; } @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
XML Configuration
If we use XML configuration, the content of the exampleApplicationContext-web.xml file looks as follows:
i<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <mvc:annotation-driven/> <mvc:resources mapping="/static/**" location="/static/"/> <mvc:default-servlet-handler/> <context:component-scan base-package="net.petrikainulainen.spring.testmvc.common.controller"/> <context:component-scan base-package="net.petrikainulainen.spring.testmvc.todo.controller"/> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException">error/404</prop> <prop key="java.lang.Exception">error/error</prop> <prop key="java.lang.RuntimeException">error/error</prop> </props> </property> <property name="statusCodes"> <props> <prop key="error/404">404</prop> <prop key="error/error">500</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> </bean> </beans>
Configuring the Test Context
The configuration of our test context has two responsibilities:
- It configures a MessageSource bean which is used by our controller class (feedback messages) and Spring MVC (validation error messages). The reason why we need to do this is that the MessageSource bean is configured in the "main" configuration class (or file) of our application context configuration.
- It creates a TodoService mock which is injected to our controller class.
Let's find out how we configure our test context by using Java configuration class and XML configuration file.
Java Configuration
If we configure our test context by using Java configuration, the source code of the TestContext class looks as follows:
import org.mockito.Mockito; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; @Configuration public class TestContext { @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages"); messageSource.setUseCodeAsDefaultMessage(true); return messageSource; } @Bean public TodoService todoService() { return Mockito.mock(TodoService.class); } }
XML Configuration
If we configure our test context by using an XML configuration, the content of the testContext.xml file looks as follow:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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"> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n/messages"/> <property name="useCodeAsDefaultMessage" value="true"/> </bean> <bean id="todoService" name="todoService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="net.petrikainulainen.spring.testmvc.todo.service.TodoService"/> </bean> </beans>
Configuring The Test Class
We can configure our test class by using one of the following options:
- The Standalone configuration allows us to register one or more controllers (classes annotated with the @Controller annotation) and configure the Spring MVC infrastructure programatically. This approach is a viable option if our Spring MVC configuration is simple and straight-forward.
- The WebApplicationContext based configuration allows us the configure Spring MVC infrastructure by using a fully initialized WebApplicationContext. This approach is better if our Spring MVC configuration is so complicated that using standalone configuration does not make any sense.
Let's move on and find out how we can configure our test class by using both configuration options.
Using Standalone Configuration
We can configure our test class by following these steps:
- Annotate the class with the @RunWith annotation and ensure that test is executed by using the MockitoJUnitRunner.
- Add a MockMvc field to the test class.
- Add a TodoService field to the test class and annotate the field with the @Mock annotation. This annotation marks the field as a mock. The field is initialized by the MockitoJUnitRunner.
- Add a private exceptionResolver() method to the class. This method creates a new SimpleMappingExceptionResolver object, configures it, and returns the created object.
- Add a private messageSource() method to the class. This method creates a new ResourceBundleMessageSource object, configures it, and returns the created object.
- Add a private validator() method to the class. This method creates a new LocalValidatorFactoryBean object and returns the created object.
- Add a private viewResolver() method to the the class. This method creates a new InternalResourceViewResolver object, configures it, and returns the created object.
- Add a setUp() method to the test class and annotate the method with the @Before annotation. This ensures that the method is invoked before each test. This method creates a new MockMvc object by calling the standaloneSetup() method of the MockMvcBuilders class and configures the Spring MVC infrastructure programmatically.
The source code of our test class looks as follows:
import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.context.MessageSource; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; import java.util.Properties; @RunWith(MockitoJUnitRunner.class) public class StandaloneTodoControllerTest { private MockMvc mockMvc; @Mock private TodoService todoServiceMock; @Before public void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(new TodoController(messageSource(), todoServiceMock)) .setHandlerExceptionResolvers(exceptionResolver()) .setValidator(validator()) .setViewResolvers(viewResolver()) .build(); } private HandlerExceptionResolver exceptionResolver() { SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException", "error/404"); exceptionMappings.put("java.lang.Exception", "error/error"); exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404"); statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver; } private MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("i18n/messages"); messageSource.setUseCodeAsDefaultMessage(true); return messageSource; } private LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); } private ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
Using the standalone configuration has two problems:
- Our test class looks like a mess even though our Spring MVC configuration is rather simple. Naturally, we could clean it up by moving the creation of Spring MVC infrastructure components into a separate class. This is left as an exercise for the reader.
- We have to duplicate the configuration of Spring MVC infrastructure components. This means that if we change something in the application context configuration of our application, we must remember to do the same change to our tests as well.
Using WebApplicationContext Based Configuration
We can configure our test class by following these steps:
- Annotate the test class with the @RunWith annotation and ensure that the test is executed by using the SpringJUnit4ClassRunner.
- Annotate the class with the @ContextConfiguration annotation and ensure that the correct configuration classes (or XML configuration files) are used. If we want to use Java configuration, we have to set the configuration classes as the value of the classes attribute. On the other hand, if we prefer XML configuration, we have to set the configuration files as the value of the locations attribute.
- Annotate the class with the @WebAppConfiguration annotation. This annotation ensures that the application context which is loaded for our test is a WebApplicationContext.
- Add a MockMvc field to the test class.
- Add a TodoService field to the test class and annotate the field with the @Autowired annotation.
- Add a WebApplicationContext field to the test class and annotate the field with the @Autowired annotation.
- Add a setUp() method to the test class and annotate the method with the @Before annotation. This ensures that the method is called before each test. This method has responsibilities: it resets the service mock before each test and create a new MockMvc object by calling the webAppContextSetup() method of the MockMvcBuilders class.
The source code of our test class looks as follows:
import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.Mockito; 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; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) //@ContextConfiguration(locations = {"classpath:testContext.xml", "classpath:exampleApplicationContext-web.xml"}) @WebAppConfiguration public class WebApplicationContextTodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; @Autowired private WebApplicationContext webApplicationContext; @Before public void setUp() { //We have to reset our mock between tests because the mock objects //are managed by the Spring container. If we would not reset them, //stubbing and verified behavior would "leak" from one test to another. Mockito.reset(todoServiceMock); mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } }
The configuration of our test class looks a lot cleaner than the configuration which uses standalone configuration. However, the “downside” is that our test uses the full Spring MVC infrastructure. This might be an overkill if our test class really uses only a few components.
Summary
We have now configured our unit test class by using both the standalone setup and the WebApplicationContext based setup. This blog post has taught us two things:
- We learned that it is important to divide the application context configuration in a such way that we can reuse parts of it in our tests.
- We learned the difference between the standalone configuration and the WebApplicationContext based configuration.
The next part of this tutorial describes how we can write unit tests for "normal" Spring MVC controllers.
P.S. You can get the example application of this blog post from Github.
I love SpringMVC. Since controllers are also POJO's, testing is easy just like testing any other class. And have options like pointed above
Thank's! Very structured and easy to understand!
You are welcome! I am happy to hear that this blog post was useful to you.
Nice, thanks for showing how to set MockMvc's view resolver programatically.
Maybe you should also mention (or did you and I overlooked it?) that this project is usable even if you are stuck with a spring version lower than 3.2.x.
Just use this project (its basically the same as this code base has been imported into the core).
https://github.com/spring-projects/spring-test-mvc
Actually I didn't realize that it would be useful to mention it here. I will update this blog post later today. Thanks for pointing this out!
Thanks for this post,
I have a little question though :
In the setUp() method which is executed before each test (@before), a MockMvc is built.
I totally understant why the todoServiceMock has to be reset before each test, but what about the MockMvc ?
Would it not be better to build the MockMvc once for all the tests (@beforeClass) ?
What's the point/utility of rebuilding a MockMvc before each test of the test class ?
Maybe i missed something here, i'm relatively new to spring mocking strategy :)
That is a GREAT question. To be honest, I create a new
MockMvc
object for each test case just because this is the way it is done in the reference manual of Spring Framework.However, now that you mentioned it, I started to wonder if this is really necessary. Unfortunately I cannot answer to your question right now, but I promise that I will try to find an answer to your question. I will let you know if I find it.
Actually I've seen both ways. After a little research in spring sources, I saw it two times in the @beforeClass. So I guess it's ok.
Example :
https://github.com/spring-projects/spring-test-mvc/blob/master/src/test/java/org/springframework/test/web/server/samples/context/WarRootDirectoryTests.java
Anyway, thank you, these tutorials have been and will be really helpful.
After I gave this some thought and did some testing, I noticed that using the
@BeforeClass
annotation isn't very handy for two reasons:setUp()
method is invoked. This means that you cannot use theWebApplicationContext
based setup (unless you build the web application context yourself). Of course you can always use the standalone setup as long as you remember to reset your mock objects in your test methods.In other words, it seems that
I did some testing too. And yeah, with the @beforeClass approach I noticed the same issues as you. I'm back to the MockMvc in the @before method.
Thank you for taking some time to take a look a this and give the proper answer to my question.
You are welcome! It is always nice to help someone out (and often I end up learning new things as well).
hai petri, thanks for structured explanation of the spring web Mvc
could plz explain why spring come into picture ? what problems java faced before spring frame work ? And how spring solved those problems ?
IMO the main reason why the Spring Framework became popular was that EJB 2 sucked big time. Writing EJB 2 applications was a complex task and the only way to configure them was to use monstrous XML files. In other words, the only other option, which provided "everything", was so horrible that developers started to use Spring Framework for writing enterprise applications.
You might want to read an article titled: Why Use the Spring Framework. However, you should remember that things have changed a lot, and the newer Java EE versions are totally different beasts.
Hi Petri,
I was following along pretty well until the Test Context configuration. Firstly, at the start of this tutorial I saw that our TodoController declared a TodoService variable. I assumed that the TodoService class would be covered at some time in the tutorial, but it was not. So I lost you on this part: .
Am I missing something? Where is the TodoService class code? or did I miss a code download somewhere?
Thanks
Hi Tyler,
The reason why I didn't describe the
TodoService
interface in this blog post was that you don't have to know what it is (yet).The next parts of this tutorial shed some light to this matter by describing the implementations of the tested controller methods. This tells you what service methods you have to mock in your unit tests.
By the way, you can get the example application of this blog post from Github.
Hey Petri!
Excellent Tutorial!
I have an issue for which I request your help.
By default, the @WebApplicationContext looks for the main spring XML file in src/main/webapp in a maven project. How do we configure it for non-maven projects.
I tried this:
@ContextConfiguration({ "classpath*:mvc-dispatcher-servlet.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration({ "classpath*:mvc-dispatcher-servlet.xml" } )
public class LoginControllerTest {
// some code
}
If the
mvc-dispatcher-servlet.xml
file is found from the "root" of your classpath, you can configure your test by using this code:Check out the following Javadocs for more details about this:
@ContextConfiguration.locations
describes what kind of values you can set as the value of thelocations
attribute of the@ContextConfiguration
annotation.AbstractContextLoader.modifyLocations()
describes how a location string is interpreted at runtime.@WebAppConfiguration
annotation.I hope that this answered to your question.
Hi Petri,
Above code with spring 4.0.5 , gives "java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig" exception with below servlet api maven dependency
javax.servlet
servlet-api
3.0-alpha-1
provided
Issue was fixed by including maven dependency for "geronimo-servlet_3.0_spec" (ref. https://jira.spring.io/browse/SPR-11049)
This was very helpful! Thank you
Hi Petri,
I used your tutorial for setting up Spring Social and am now trying to unit test following this tutorial. The problem I am running into is that Spring-Social's SocialConfiguration class that we extend to setup the social side of the house has a method "usersConnectionRepository" which calls the SocialConfigurer.getUsersConnectionRepository(connectionFactoryLocator. Obviously I don't want a JDBCConnectionFactory being setup and called for unit tests but when I add a bean class to override it in my TestContext class Spring throws an exception say it found 2 matching beans and expected only one. Any help you can provide is appreciated.
Hi Justen,
You can probably solve your problem by using bean definition profiles.
You might want to take a look at the example application because it has comprehensive unit and integration tests. However, writing them was a bit tricky. That is why I wrote two blog posts which explain this process:
If these blog posts don't help you to solve your problem, you could create a Gist which explains your test configuration (it feels a bit different than mine because I didn't use the
SocialContext
class in my unit tests).Thanks, nice post
Hello Petri,
Thanks for the post. I have been following the same.
I am writing unit test based on java config approach.
I am able to deploy and test the application via rest client such as postman.
But with Junit I am not sure why I am getting the below error. Is this a configuration problem. Please let me know your input.
Kind Regards
Sanjeev.
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/SessionTrackingMode
Update: I removed the unrelated part of the stack trace - Petri
I found this blog post which describes a very similar problem and provides a solution to it.
Is the servlet-api jar file in the classpath when you run your unit tests?
Why test code is far longer than code to be tested ?
Isn't it a little problem ?
Who says more code, says more bugs, isn't it ?
Anyway good job
If you are referring to the test methods described in the second and third part of this tutorial, the reason is that those methods test too much. Nowadays I split the test methods into smaller test methods which test only one thing.
But I think that it is quite common that the LOC of your test code is a lot bigger than the LOC of your production code. This happens if you write comprehensive tests for your code.
Yes. The test methods described in later parts of this tutorial should be splitted into smaller methods.
Yes. That is why I split my test methods into smaller methods. These methods are typically much simpler which makes it easier to verify that there are no bugs.
Thanks. Also, thank you for asking this important question.
Petri - love the articles; excellent resource. a question - presumably when in normal (non-test) running, something is component scanning to find the 'real' TodoService (web context class?). How do you stop that scanning occurring in testing mode and to use the mock TodoService instead - i cant seem to separate the configs enough to get that to happen - i get the real and mock beans being created and getting startup errors.
Thanks again.
Hi Martin,
Thank you for your kind words. I really appreciate them.
I separate the application context configuration classes of my application in a such way that the web layer is configured in a single class that also configures the component scan for controllers and other components that belong to the web layer. If I do this, I can then create a separate application context configuration class for my tests that creates the "mock beans".
If you take a look at the application configuration of my example application, you notice that
ExampleApplicationContext
class configures the component scan for components that belong to the service layer.WebAppContext
class configures the component scan for components that belong to the web layer.If I write unit tests that use the only the
WebAppContext
class and the configuration class that creates the mock beans, I can scan only the tested classes.A word of warning though:
When I wrote this tutorial, I used the web application context based setup because it seemed easier than standalone setup. Although this is true for simple applications, maintaining the application context configuration class that creates the "mock beans" can be a bit painful if you are writing tests for a real life application that has a lot beans that should be mocked.
In other words, sometimes it might be wiser to use the standalone configuration (especially in unit tests).
Hi Petri, thanks a lot for your tutorial, it really helps me.
But unfortunately, i encounter a problem. I noticed that in your project on Github, you have several ApplicationContext files, but you don't mention them in this blog. i do not write any ApplicationContext.java files, and i get this error when testing.
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: java.lang.IllegalStateException: Cannot load configuration class:
Does this mean i need to write some ApplicationContext.java files like you do in the project?
thanks
Hi,
Thank you for your kind words. I really appreciate them.
You are right. You have to configure the application context of your application by using either Java configuration classes or XML configuration files.
Although you can write unit tests for your controllers by using the standalone configuration (scroll down and look for a string 'Setup Options'), you cannot run your application if you haven't configured its application context.
The example application of this blog post has unit tests that use the standalone configuration and the web application context based configuration.
If you have further questions, don't hesitate to ask them!
Hi Petri,
exceptation is not invoked it returns null
using mockmvc with standalone,@Runwith(MockitJunitRunner.class)
i debugged the code,the exception which we set is not called ?
what is i am missing
Thanks
Hi,
Do you mean that
getxxx()
method of themyUtil
class is not invoked at all.getxxx()
method of themyUtil
class is invoked, but it returns null.It is kind of hard to say what is wrong because you omitted the source code of your controller method. Could you add the source code of your controller method here?
By the way, you might want to read a blog post titled: Mockito: Why You Should Not Use InjectMocks Annotation to Autowire Fields. It explains why you should use constructor injection instead of field injection (I have written about this too).
its mockitocglibproxy called which in term invokes mokito api handler which retruns false due to internal stubbed array is null
The argument used in [that article](http://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields) is very weak: it simply says that, since Mockito will fail *silently* when a new dependency (the `AuditService`) is added, calling the constructor directly in the test would be better as you have to do it explicitly.
But that is just a specific Mockito limitation, which happens to lack support for annotations like @Autowired. If it had (like another testing library - JMockit - does), then the test would fail with a nice exception message about the missing dependency.
Constructor injection, in fact, is a bad choice, compared to injection into annotated fields, for the following reasons:
1. Constructor injection requires more code: you need to have instance fields for the injected dependencies, and also an assignment to each field in the injection constructor.
2. Most dependencies are private, not meant to be exposed in a public API (the annotated constructor). For example, a DAO, or a JPA EntityManager. Having only the field for each such dependency (rather than the field + the constructor parameter) is simpler, takes less code, and preserves encapsulation which would otherwise be broken by exposing the dependency through a public constructor.
3. Java EE has made the choice of prefering field injection, with constructor/setter injection being supported only as an afterthought. From my experience, most new projects nowadays and in the future will be using Java EE instead of Spring.
Interesting comment. Here are my thoughts about this:
True, but this is a good thing.
You can define a public API by using interfaces and hiding the actual implementations.
So, what happens if you remove the bean that is required by another bean? The field injection doesn't really encapsulate anything. The only thing that happens is that the dependencies of the created bean are hidden (but still required). If you really want to encapsulate the implementation details of a bean, you should use interfaces (but only if you have more than one implementation OR you want to publish the API of a module).
This constructor injection vs. field injection discussion has got nothing to do with Java EE or Spring. I think it is great that Java EE works for you. Keep using it.
thanks for your suggestions,
my controller method and agruments are same which i am trying to set in mockito.when -exceptation.
i will try your suggestion instead of inject mock i will try to set using ReflectionTestUtil.setField() method and come back
one more question i debugged mockito api it is not stubbing utils class, -is this due to util class has also some autowire components
Do you create a new
MyController
object in your test class? The reason why I am asking this is that your sample code does not create it.If you use the
@InjectMocks
annotation, you have to use it as follows:Unfortunately it is impossible to say what is wrong without seeing the source code of the tested controller and the
MyUtil
class. However, I think that the dependencies of theMyUtil
class have got nothing to do with this problem because I have been able to mock classes that use other components.how to mock service method having request as argument in the controller.
i used the mockmvc with test class is annoted with @mokitojunitrunner as advised in tutorial
mokito always returns false because in mockmvc framework ,it creates MockHttpServletRequest but actual service method has HttpServletRequest due to this mismatch in object type -i debugged the findout the above reason .
could you please suggest how to deal with this..
Hi,
Here is a little example that demonstrates how you can solve your problem.
First, my controller looks as follows:
Second, my service class looks as follows:
And my passing test looks as follows:
I hope that this answered to your question. If not, feel free to ask additional questions.
thank you and really appreciate
You are welcome! I am happy to hear that this tutorial was useful to you.
Hi Petri,
Your blog is awesome and providing good amount of unit and integration testing.
Thank you for your kind words! I really appreciate them.
Hello, Petri.
Nice tutorial, but what should I do if I have an @ImportResource annotation in my Controller (for example @ImportResource("classpath:controllerContext.xml") where controllerContext.xml contains the same context as I use in testing) ? Everything is ok if I've got it, tests work nice, but when I delete it, tests do not pass due to
java.lang.AssertionError: Content type not set
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:39)
My controller needs @ImportResource to get configuration, without it I've got
org.springframework.beans.factory.BeanCreationException
Thanks.
Why you added the
@ImportResource
annotation to your controller class instead of your application context configuration class? The reason why I ask this is that typically that annotation is used to import XML configuration files from application context configuration classes (@Configuration
classes).Unfortunately it is kind of hard to give you a better answer without seeing your code, but it seems that the problem is somehow related to the way you configure your application / tests. If you can share your application context configuration classes, XML configuration files, and your test class, I can probably give you a better answer.
Hi Petri,
Thank you for this truly outstanding article. And the followup looks even better! Will have continue studying that one soon.
I am relatively new with Spring and Spring MVC (somewhat less with Java, but still..), so please don't shoot me if this is a stupid question.
You explained how to setup the test context config using the standalone configuration and reusing (part of) the "production" WebApplicationContext.
Now, in a test project I had configured a service bean, used by 2 controllers. This bean is defined in my WebConfig (WebMvcConfigurerAdapter) class (maybe that's not a good idea?).
As I wanted to use a mock version of those services, I was thinking of a way to solve this and I came up with a variation on your idea to point to both the existing WebConfig and an additional test config.
It looks like this:
@ContextConfiguration(classes=TestContext.class)
My tests seems to be working fine.
Do you think this an acceptable approach?
Thank you for your reply.
Best regards,
Johan
I changed the formatting a bit so that your question looks "better" - Petri
Hi Johan,
Thank you for your kind words. I really appreciate them!
If you want to use the
WebApplicationContext
based setup, I recommend that you divide your application configuration context configuration in a such way that the@Configuration
class which configures the web layer configures only the beans that belong to the web layer.If you don't do this, replacing the service beans with mocks becomes a bit "messy". In your case this is not a huge problem because you can extend the
WebConfig
class and override its@Bean
methods. However, if you would have used the@ComponentScan
annotation to scan the service beans, you would be in trouble.In other words, you might want to use the following setup:
WebConfig
class configures only the web layer.ServiceConfig
class (awful name, don't use this) class configures the service beans.TestServiceConfig
class configures the required mock service beans.Now you can create a simple application context configuration class that looks as follows:
As you can see, this class simply imports the required application context configuration classes and you are good to go. What do you think about this approach?
Also, I don't use the
WebApplicationContext
based setup in my unit tests anymore. I realized that creating mock beans becomes pain in the ass if you have to mock "many" service beans. If you use theWebApplicationContext
based setup, you have to remember to add your mock beans to theTestServiceConfig
class or your tests will fail.That is why I use standalone configuration for unit tests and
WebApplicationContext
based configuration for integration tests.Hi Petri,
Thank you very much for your answer, which makes more than a lot of sense.
I will certainly take your advice at heart in order to avoid any unnecessary pain in the ass. ;-)
I wish I had more time to investigate your blog, you seem to procure mind blowing articles!
...well, maybe one article a day... ;-)
Thanks again and best regards,
Hi Johan,
You are welcome. I am happy to hear that my answer was useful to you. Also, if you have any other questions, don't hesitate to ask them.
All the best.
Hi, have a good day.
I have a project that uses xhtml files i meke a war file, but i don't use spring, i use jsf. I would like to know if i can use your example to test my project.
Thanks!!
Unfortunately you cannot use Spring MVC Test if you are writing tests for a JSF application. Check out JSFUnit and Arquillian. Also, you might want to read a blog post titled: Testing JSF and RichFaces with Arquillian - Part I - Used Technologies (check out the other parts as well).
hey i got error on TodoService in TodoController class please resolve it sir
What kind of an error do you get?
can not resolved the type of TodoService sir
Ah. The
TodoService
interface declares service methods that provide CRUD operations for todo entries. Also, you can get the example application of this blog post from Github.getting null pointer exception at
Did you create the
mockMvc
object by using application context based configuration or standalone configuration? Also, are you sure that it is not null?If the
mockMvc
object is not null, you can the print the response (and the other relevant information) by using this code:This should help you to solve your problem.
hello petri,
Thank you for your replay but still I get the same eexception, yes I have created my mockMvc object ,Im very new to this concept I defenately know that I have missed something important, but not able to trace it out, and also please tell me that how can I know that mock object is null or not null
waiting for your response
Thanks
private MockMvc mockMvc;
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
Can I see the stacktrace? I don't know why your test throws NPE, and I suspect that the stacktrace could help me to figure out what is going on.
Hi Petri,
Your way of explanation is good , but
The first application configuration class is called ExampleApplicationContext and it is the “main” configuration class of our application.
The third configuration class is called PersistenceContext and it contains the persistence configuration of our application.
I have doubt about above two sentences
Where is the source code about ExampleApplicationContext and PersistenceContext
Hi,
You can get the source code from Github.
Hi Petri,
Thanks for the brilliant tutorial. In addition to learning more about Spring MVC testing, this blog has helped to learn more about the user of Mockito and Hamcrest as well.
Regards,
ArunM
You are welcome! I am happy to hear that my Spring MVC Test tutorial was useful to you.
getting problem while testing and the error is autowiring dependency injection problem for the test class I wrote.......
provide some solution.....
thankyou
I need to see the stack trace before I can figure out what is wrong. Can you add it into a new comment?
Hi Petri,
I'm trying to test my controller using the Standalone approach but I'm struggling injecting MessageSource.
In my case the controller does not use "constructor injection". MessageSource is injected via setter injection.
my controller looks like:
...sorry I hit submit by mistake
anyway how my standalone test configuration should be in order to inject the MessageSource ?
Thanks
Gaetano
Hi,
If you use setter injection, you can simple create the controller object and invoke the correct setter. The
setUp()
method, which uses this approach, looks as follows:However, the code you pasted uses field injection. In this case you need to "inject" the
MessageSource
object by using a utility class calledReflectionTestUtils
. ThesetUp()
method, which injects theMessageSource
object by using that utility class, looks as follows:If you have any additional questions, don't hesitate to ask them.
Hi Petri,
Thanks a lot for your help - it worked
You might hear from me in a couple of days, when I'll try to tackle the approach based on WebApplicationContext :)
Regards
Gaetano
You are welcome! Also, it will be fun to see what I forgot to mention in this rather old blog post ;)
how to overcome the circular view path exception, because I have my uri and the return type string are the same.
@RequestMapping(value = "newemployee", method = RequestMethod.GET)
public ModelAndView newEmployeeGet(HttpServletRequest request) {
String ss = "[\"1-1-2016\",\"2-1-2016\",\"3-1-2016\",\"4-1-2016\",\"5-1-2016\",\"6-1-2016\",\"7-1-2016\",\"8-1-2016\"]";
map.put("holidays", ss.toString());
return new ModelAndView(prefixResult + "newemployee", map);
}
cannot be changing the methods now. But I have to do something to get rid of this circular view path exception. How do I do it.
Check out this StackOverflow answer. It provides a good description of this problem.
Unfortunately I am not sure how you can fix this because I am not sure what you want to do. Do you want to redirect the request to another controller method or do you simple want to render a view? If you want to render a view, what view technology are you using?
Hi Petri,
I found your post very useful. I am trying to test my REST APIs using Spring test + Mockito as you suggested. I am trying using standAloneSetup method. But some of my DTO classes have date field and for same I am using JSON serializer for converting it into my desired format. Usually this format comes from our properties file and I have fetched format from properties file as below.
public class CustomDateSerializer
extends JsonSerializer {
@Value("${dateFormat}")
private String dateFormat;
@Value("${timeFormat}")
private String timeFormat;
so in run time it gets properties file in class path fetches format properly but while running test it is not able fetch OR may be expression is not even getting evaluated so its getting format fields null and it fails request to status 500.
How can I resolve this issue?
Hi,
Have you configured the
MappingJackson2HttpMessageConverter
object and provided it to the createdMockMvc
object? If you haven't done this, you should give it a shot. If you don't know how you configure theMappingJackson2HttpMessageConverter
object, take a look at this blog post.Great tutorial, keep going on!
Thanks!
Hello There,
Great tutorial!
I am curious why did you strike through MockitoJUnitRunner
Hi,
I use a plugin that identifies dead links and marks them as dead. I assume that this plugin added the strike though "style" to the link because the website was down when it checked the link.
hi,Petri,I see someone said "Even if you’re just using the Spring Context to auto-wire dependencies, your test is an integration test" in https://springframework.guru/mockito-mock-vs-spy-in-spring-boot-tests/ .
so I think the adoption of standaloneSetup or webAppContextSetup method can be see as integration test, what's your opinion?
Hi,
The difference between the application context based configuration and the standalone configuration is that if you use the standalone configuration, you can use only the components that are relevant for the test case. In other words, the standalone configuration helps you to minimize the size of the tested unit.
If you think about the traditional definitions of unit and integration testing, a test that uses Spring MVC Test framework is not a unit test because you are not testing the controller method in isolation. However, I don't use the web application context based setup in my "unit tests" anymore because the standalone setup helps me to highlight the dependencies of the system under test.
P.S. I use the terms integration and unit tests mainly because they are more familiar than terms slow and fast tests (although the latter terms are better IMO).
Thanks Petri...This is very useful!
You are welcome!
Thank you man, God bless you for sharing knowledge...
You are welcome!
Do you know that some of the code samples on this range of tutorial pages have incorrect escaping? For example:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
It makes it a bit more difficult to follow.
Nope. I wasn't aware of that. It's fixed now. I have to check what's causing this because it seems that this behavior is totally random. I guess that some WP plugin is not working correctly. In any case, thank you for reporting this issue.
Hi,
What is the use of TodoDTO.java
Hi,
The
TodoDTO
class is a data transfer object which helps us to hide the internal data model of our application from the outside world.