Testing Spring MVC with Spring Boot 1.4: Part 1
7 CommentsLast Updated on June 24, 2019 by Simanta
In my earlier Integration Testing with Spring and JUnit post, I discussed how to test a service bean facade with JUnit. I also posted a video, titled Testing Spring with JUnit on YouTube. In post, Spring Boot Web Application – Part 3 – Spring Data JPA, I showed how to test a Spring Data JPA repository of a Spring MVC application.
It has been more than a year now since I wrote these posts and posted the video. Since then, there’s been an exciting overhaul of testing support for Spring Boot MVC applications. The Spring Boot 1.4 release includes a number of exciting new testing features for our use.
In this post, I will look the new Spring MVC testing features. And I’ll show you how to put them to use.
Spring Boot 1.4 Testing Enhancements
In Spring Boot 1.3, there’s a lot of choices to write tests for a Spring MVC application. One option to set Spring MVC is shown in my earlier post here. In this post on Spring Boot 1.3, the @RunWith
annotation with @ContextConfiguration
is used to test for a business service façade, like this:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ProductServiceTestConfig.class}) public class ProductServiceImplIT { private ProductService productService; @Autowired public void setProductService(ProductService productService) { this.productService = productService; } @Test public void testGetProduct(){ /*Test code*/ } }
Another method I used in the post here is a combination of the @RunWith
annotation with @SpringApplicationConfiguration
to test a Spring Data JPA repository, like this:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = {RepositoryConfiguration.class}) public class ProductRepositoryTest { private ProductRepository productRepository; @Autowired public void setProductRepository(ProductRepository productRepository) { this.productRepository = productRepository; } @Test public void testSaveProduct(){ /*Test code*/ } }
There are several other approaches you can check in the official Spring blog here.
The testing approaches I used above are actually integration tests. A pure unit test shouldn’t create and load Spring Context.
Spring Boot 1.4 replaces these various testing approaches that via a single @SpringBootTest
annotation for regular integration tests.
Prior to Spring Boot 1.4, I found Spring was lacking a simplified unit testing approach. This is really no surprise. The Spring team is always creating. Spring and Spring Boot offers a number of testing options. Due to innvotation, the testing options have evolved over time. In Spring Boot 1.4, the Spring committers took some time to clean testing up. They gave us much simpler options to use for testing Spring Boot applications. For example, a simple approach to unit test a controller having @Autowired
external services without having to load Spring Context was lacking. With Spring Boot 1.4, it’s now possible.
Another missing piece that Spring Boot 1.4 tackels, is the ability to test portions (slices) of code. This can be done without the need to fire up a server. And with out the need to load up the entire Spring Context. Spring Boot 1.4 does this through the new Test Slicing feature that is designed to set-up a narrow slice of the Spring Context. This makes testing single ‘slices’ much easier. You can now focus on testing specific slices of your application. For example:
For example:
- MVC slice: Controller code through the
@WebMvcTest
annotation - JPA slice: Spring Data JPA repository code through the
@DataJpaTest
annotation - JSON slice: JSON serialization code through the
@JsonTest
annotation
This may not seem like much at first glance. But when you have a large application starting the Spring context in testing, it is time consuming. Context loads can really increase your build time.
Let’s start putting the new test features to use.
The Application Under Test
I wrote a series of posts on Spring MVC starting off from Spring Boot Web Application – Part 1 – Spring Initializer. In the last post of the series, Spring Boot Web Application – Part 4 – Spring MVC, I completed creating a Spring MVC application to perform Create, Read, Update, and Delete (CRUD) operations.
In this post, I’ll show you how to write tests for the controllers of the same Spring MVC application.
If you are new to Spring MVC, you should go through my series on Spring MVC starting here.
You can also download the source code of the application available on GitHub here to follow along this post.
It’s a pretty simple example of a Spring Boot MVC application consisting of the following primary components:
Product
: The domain object, which is a JPA entityIndexController
: Returns theindex.html
Thymeleaf template for aGET
request to the application rootProductController
: Contains number of actions methods that useProductService
to perform CRUD operations via the repository modelProductRepository
: A Spring Data JPA repositoryProductService
: A business service façade interfaceProductServiceImpl
: A business service façade implementation annotated with@Service
With the Spring Boot MVC application that will be under test in place, lets start by writing few tests for the controllers.
Maven Dependencies
The testing features we’re looking at were introduced in Spring Boot 1.4. The version of Spring Boot we’ll be using is 1.4.0.RELEASE.
Here is the complete Maven POM that we’ll use.
pom.xml
4.0.0 guru.springframework springmvctest 0.0.1-SNAPSHOT jar springmvctest Examples of Spring MVC Test org.springframework.boot spring-boot-starter-parent 1.4.0.M3 UTF-8 1.8 org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web compile org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.webjars bootstrap 3.3.4 org.webjars jquery 2.1.4 org.springframework.boot spring-boot-maven-plugin spring-milestones http://repo.spring.io/milestone spring-milestones http://repo.spring.io/milestone
Unit Testing Spring MVC Controllers
MockMvc
has been around since Spring 3.2. This providing a powerful way to mock Spring MVC for testing MVC web applications. Through MockMvc
, you can send mock HTTP requests to a controller and test how the controller behaves without running the controller within a server. You can obtain a MockMvc
instance through the following two methods of MockMvcBuilders
:
standaloneSetup()
: Registers one or more@Controller
instances and allows programmatically configuring the Spring MVC infrastructure to build aMockMvc
instance. This is similar to plain unit tests while also making it possible to focus tests around a single controller at a time.webAppContextSetup()
: Uses the fully initialized (refreshed)WebApplicationContext
to build aMockMvc
instance. This lets Spring load your controllers as well as their dependencies for a full-blown integration test.
Pro Tip: Whenever possible, I will try to use standaloneSetup()
for my SpringMVC tests. Your tests will remain true unit tests and stay blazing fast!
This is the IndexController
that we are going to test:
IndexController.java
package guru.springframework.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { @RequestMapping("/") String index(){ return "index"; } }
For our purpose, we’re starting with standaloneSetup()
to test this IndexController
.
The test class is this.
IndexControllerTest.java
package guru.springframework.controllers; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; @RunWith(SpringRunner.class) public class IndexControllerTest { private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(new IndexController()).build(); } @Test public void testIndex() throws Exception{ this.mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("index")) .andDo(print()); } }
The test class above is a JUnit test. If you are new to JUnit, you should go through my series on unit testing with JUnit, starting from here. In the test class above, observe the new Spring Boot 1.4 test runner, named SpringRunner
that we specified for @RunWith
in Line 20. Under the hood, both SpringRunner
and its predecessor SpringJUnit4ClassRunner
are the same. SpringRunner
is only the new name for SpringJUnit4ClassRunner
– to just make it easy on the eyes.
In the @Before
annotated method that runs before all @Test
method, we programmatically constructed a MockMvc
instance after registering the IndexController
instance.
In the @Test
method, we used the MockMvc
instance to verify the following behavior of IndexController
:
- HTTP status code
200
is returned - The name of the returned view is
index
Finally, by using andDo(print())
, we get the following output on the console
Testing the Spring MVC Slice
The unit test we wrote were for some basic expectations of the controller. Let’s write some more specific tests, this time to test ProductController
. This time we’re going to use webAppContextSetup()
to build MockMvc
.
For a quick recap, the ProductController
class is this.
ProductController.java
package guru.springframework.controllers; import guru.springframework.domain.Product; import guru.springframework.services.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class ProductController { private ProductService productService; @Autowired public void setProductService(ProductService productService) { this.productService = productService; } @RequestMapping(value = "/products", method = RequestMethod.GET) public String list(Model model){ model.addAttribute("products", productService.listAllProducts()); return "products"; } @RequestMapping("product/{id}") public String showProduct(@PathVariable Integer id, Model model){ model.addAttribute("product", productService.getProductById(id)); return "productshow"; } @RequestMapping("product/edit/{id}") public String edit(@PathVariable Integer id, Model model){ model.addAttribute("product", productService.getProductById(id)); return "productform"; } @RequestMapping("product/new") public String newProduct(Model model){ model.addAttribute("product", new Product()); return "productform"; } @RequestMapping(value = "product", method = RequestMethod.POST) public String saveProduct(Product product){ productService.saveProduct(product); return "redirect:/product/" + product.getId(); } @RequestMapping("product/delete/{id}") public String delete(@PathVariable Integer id){ productService.deleteProduct(id); return "redirect:/products"; } }
We will start by testing the behavior of ProductController.list()
method. For a GET
request to /product
, we will perform the following verification:
- The
ProductService
mock is not null - The HTTP status code
200
is returned - The returned content type is
text/html;charset=UTF-8
- The name of the returned view is
products
- The view contains the
Spring Framework Guru
string
Here is the test class.
ProductControllerTest.java
@RunWith(SpringRunner.class) @WebMvcTest(controllers = ProductController.class) public class ProductControllerTest { private MockMvc mockMvc; @Autowired private WebApplicationContext webApplicationContext; @MockBean private ProductService productServiceMock; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test public void testList() throws Exception { assertThat(this.productServiceMock).isNotNull(); mockMvc.perform(MockMvcRequestBuilders.get("/products")) .andExpect(status().isOk()) .andExpect(content().contentType("text/html;charset=UTF-8")) .andExpect(view().name("products")) .andExpect(MockMvcResultMatchers.view().name("products")) .andExpect(content().string(Matchers.containsString("Spring Framework Guru"))) .andDo(print()); } }
As we are testing the MVC slice of the application (testing whether the ProductController
is working as expected), we used the @WebMvcTest
annotation combined with @RunWith(SpringRunner.class)
.
As we planned to use webAppContextSetup()
to build MockMvc
, we @autowired
WebApplicationContext
in Line 6 – Line 7 to bring it into our test. Then in line 13, we passed WebApplicationContext
as an argument to webAppContextSetup()
to build the MockMvc
instance.
Going back to the ProductController
class under test, note that the controller class is @Autowired
with ProductService
. Therefore, we used the @MockBean
annotation to define a Mockito mock for ProductService
(Line 8 -Line 9) that will be passed to the controller. If you are new to mocking in unit tests, checkout my Mocking in Unit Tests with Mockito post.
Coming back to the test, in Line 17 we used the AssertJ library to assert that the ProductService
mock is not null
.
Note: Starting with Spring Boot 1.4, AssertJ comes out-of-the-box with Spring Boot to provide a fluent assertion API with a plan to replace JUnit’s org.junit.Assert
class.
From Line 19 – Line 23, it’s all about verifying our expectations. As you can see, a lot of static methods are being used in this test method, including static methods of MockMvcRequestBuilders (get()
), MockMvcResultMatchers (status()
, content()
, and view()
), MockMvcResultMatchers (match()
), and Hamcrest Matcher’s (match()
). The last two match()
are similar and performs the same functions in our test. They exist together only to demonstrate the different approaches that can be used.
Our test method reads naturally. First it performs a
First it performs a GET
request against /products
. Then it expects that the request is successful (isOk()
asserts an HTTP 200
response code) and that the content type and name of the view is text/html;charset=UTF-8
and products
respectively. Finally, it asserts that the view contains the Spring Framework Guru
string.
When all the expectations pass, Line 24 prints the result out to the console.
The important thing to note here is that at no time is the application gets deployed to a server. The Tomcat container is not used. Instead the application runs within a mocked out Spring MVC to handle the HTTP request that we provided through the
Instead the application runs within a mocked out Spring MVC to handle the HTTP request that we provided through the MockMvc
instance.
Here is the test result in the console.
The complete output of the test sent to console is this.
MockHttpServletRequest: HTTP Method = GET Request URI = /products Parameters = {} Headers = {} Handler: Type = guru.springframework.controllers.ProductController Method = public java.lang.String guru.springframework.controllers.ProductController.list(org.springframework.ui.Model) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = products View = null Attribute = products value = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = {Content-Type=[text/html;charset=UTF-8]} Content type = text/html;charset=UTF-8 Body = <!DOCTYPE html> <html> <head lang="en"> <title>Spring Framework Guru</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link href="/webjars/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen" /> <script src="/webjars/jquery/2.1.4/jquery.min.js"></script> <link href="/css/guru.css" rel="stylesheet" media="screen" /> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="/">Home</a> <ul class="nav navbar-nav"> <li><a href="/products">Products</a></li> <li><a href="/product/new">Create Product</a></li> </ul> </div> </div> </nav> <div class="jumbotron"> <div class="row text-center"> <div class=""> <h2>Spring Framework Guru</h2> <h3>Spring Boot Web App</h3> </div> </div> <div class="row text-center"> <img src="/images/NewBannerBOOTS_2.png" width="400" /> </div> </div> </div> </body> </html> Forwarded URL = null Redirected URL = null Cookies = []
Testing Spring MVC Slice with @Autowired MockMvc
Now let’s test the behavior of showProduct()
of ProductController
. Instead of manually building MockMvc
, we’ll use a @Autowired
MockMvc
in the test and let Spring create, configure, and provide a MockMvc
for us.
This is how the test class now looks minus any @Test
method implementations.
@RunWith(SpringRunner.class) @WebMvcTest(controllers = ProductController.class) @AutoConfigureMockMvc(secure=false) public class ProductControllerTest { @Autowired private MockMvc mockMvc; @Autowired private WebApplicationContext webApplicationContext; @MockBean private ProductService productServiceMock; Product product1; @Before public void setUpProduct() throws Exception{ product1 = new Product(); product1.setId(1); product1.setProductId("235268845711068308"); product1.setDescription("Spring Framework Guru Shirt"); product1.setPrice(new BigDecimal("18.95")); product1.setImageUrl("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg"); } @Test public void testList() throws Exception { /*Test code*/ } @Test public void testShowProduct() throws Exception { /*Test code*/ } }
In the test class above, notice that we used the @Autowired
annotation on MockMvc
in Line 5 – Line 6 instead of building it manually.
An @Autowired
MockMvc
combined with @WebMvcTest(controllers = ProductController.class)
gives us a fully configured MockMvc
instance with Spring security configured to set up BASIC authentication.
At this point, if we run the ProductControllerTest.testList()
test again, we’ll encounter an authentication error, like this.
MockHttpServletResponse: Status = 401 Error message = Full authentication is required to access this resource
We’re getting the 401 response because Spring Boot is auto-configuring Spring Security for us.
To disable the Spring Security auto-configuration, we can the MockMvc
instance to disable security with @AutoConfigureMockMvc(secure=false)
in Line 3.
Note, in the @Before
method, we created and initialized a Product
domain object that we will use in the @Test
method.
The @Test
method is this:
. . . @Test public void testShowProduct() throws Exception { assertThat(this.productServiceMock).isNotNull(); when(productServiceMock.getProductById(1)).thenReturn(product1); MvcResult result= mockMvc.perform(get("/product/{id}/", 1)) .andExpect(status().isOk()) .andExpect(view().name("productshow")) .andExpect(MockMvcResultMatchers.model().attributeExists("product")) .andExpect(model().attribute("product", hasProperty("id", is(1)))) .andExpect(model().attribute("product", hasProperty("productId", is("235268845711068308")))) .andExpect(model().attribute("product", hasProperty("description", is("Spring Framework Guru Shirt")))) .andExpect(model().attribute("product", hasProperty("price", is(new BigDecimal("18.95"))))) .andExpect(model().attribute("product", hasProperty("imageUrl", is("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg")))) .andReturn(); MockHttpServletResponse mockResponse=result.getResponse(); assertThat(mockResponse.getContentType()).isEqualTo("text/html;charset=UTF-8"); Collection<String> responseHeaders = mockResponse.getHeaderNames(); assertNotNull(responseHeaders); assertEquals(1, responseHeaders.size()); assertEquals("Check for Content-Type header", "Content-Type", responseHeaders.iterator().next()); String responseAsString=mockResponse.getContentAsString(); assertTrue(responseAsString.contains("Spring Framework Guru")); verify(productServiceMock, times(1)).getProductById(1); verifyNoMoreInteractions(productServiceMock); } . . .
In the @Test
method above:
- Line 4: Performs an AssertJ assertion to test that the
ProductService
mock is notnull
. - Line 5: Uses Mockito to stub the
getProductById()
method on theProductService
mock to return the initializedProduct
instance - Line 8 to Line 15: Performs the following verifications for a
GET
request toproduct/{id}
: - The HTTP status code
200
is returned - The name of the returned view is
productshow
- The view model contains a
product
attribute - The various properties of the
product
attribute matches against the values we used to initializeProduct
- The HTTP status code
- Line 16: Returns the result as
MvcResult
- Line 19- Line 20: Uses AssertJ to assert that the content type of the response is
text/html;charset=UTF-8
- Line 22- Line 27: Uses JUnit assertions to assert that:
- The response header that
MvcResult
returns asMockHttpServletResponse
is notnull
- There is only one response header
- The response header name is
Content-Type
- The response contains the
Spring Framework Guru
string
- The response header that
- Line 29 -Line 30: Uses Mockito to verify that the
getProductById()
is called only once on theProductService
mock, and that no other methods of theProductService
mock are called during the test.
The complete test class is this:
ProductControllerTest.java
package guru.springframework.controllers; import guru.springframework.domain.Product; import guru.springframework.services.ProductService; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.math.BigDecimal; import java.util.Collection; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.web.context.WebApplicationContext; import static org.mockito.Mockito.*; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @RunWith(SpringRunner.class) @WebMvcTest(controllers = ProductController.class) @AutoConfigureMockMvc(secure=false) public class ProductControllerTest { @Autowired private MockMvc mockMvc; @Autowired private WebApplicationContext webApplicationContext; @MockBean private ProductService productServiceMock; Product product1; @Before public void setUpProduct() throws Exception{ product1 = new Product(); product1.setId(1); product1.setProductId("235268845711068308"); product1.setDescription("Spring Framework Guru Shirt"); product1.setPrice(new BigDecimal("18.95")); product1.setImageUrl("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg"); } @Test public void testList() throws Exception { assertThat(this.productServiceMock).isNotNull(); mockMvc.perform(MockMvcRequestBuilders.get("/products")) .andExpect(status().isOk()) .andExpect(content().contentType("text/html;charset=UTF-8")) .andExpect(view().name("products")) .andExpect(MockMvcResultMatchers.view().name("products")) .andExpect(content().string(Matchers.containsString("Spring Framework Guru"))) .andDo(print()); } @Test public void testShowProduct() throws Exception { assertThat(this.productServiceMock).isNotNull(); when(productServiceMock.getProductById(1)).thenReturn(product1); MvcResult result= mockMvc.perform(get("/product/{id}/", 1)) .andExpect(status().isOk()) .andExpect(view().name("productshow")) .andExpect(MockMvcResultMatchers.model().attributeExists("product")) .andExpect(model().attribute("product", hasProperty("id", is(1)))) .andExpect(model().attribute("product", hasProperty("productId", is("235268845711068308")))) .andExpect(model().attribute("product", hasProperty("description", is("Spring Framework Guru Shirt")))) .andExpect(model().attribute("product", hasProperty("price", is(new BigDecimal("18.95"))))) .andExpect(model().attribute("product", hasProperty("imageUrl", is("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg")))) .andReturn(); MockHttpServletResponse mockResponse=result.getResponse(); assertThat(mockResponse.getContentType()).isEqualTo("text/html;charset=UTF-8"); Collection responseHeaders = mockResponse.getHeaderNames(); assertNotNull(responseHeaders); assertEquals(1, responseHeaders.size()); assertEquals("Check for Content-Type header", "Content-Type", responseHeaders.iterator().next()); String responseAsString=mockResponse.getContentAsString(); assertTrue(responseAsString.contains("Spring Framework Guru")); verify(productServiceMock, times(1)).getProductById(1); verifyNoMoreInteractions(productServiceMock); } }
The complete output of the test sent to console is this:
MockHttpServletRequest: HTTP Method = GET Request URI = /product/1/ Parameters = {} Headers = {} Handler: Type = guru.springframework.controllers.ProductController Method = public java.lang.String guru.springframework.controllers.ProductController.showProduct(java.lang.Integer,org.springframework.ui.Model) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = productshow View = null Attribute = product value = guru.springframework.domain.Product@6069dd38 errors = [] FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = {Content-Type=[text/html;charset=UTF-8]} Content type = text/html;charset=UTF-8 Body = <!DOCTYPE html> <html> <head lang="en"> <title>Spring Framework Guru</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link href="/webjars/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen" /> <script src="/webjars/jquery/2.1.4/jquery.min.js"></script> <link href="/css/guru.css" rel="stylesheet" media="screen" /> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="/">Home</a> <ul class="nav navbar-nav"> <li><a href="/products">Products</a></li> <li><a href="/product/new">Create Product</a></li> </ul> </div> </div> </nav> <div class="jumbotron"> <div class="row text-center"> <div class=""> <h2>Spring Framework Guru</h2> <h3>Spring Boot Web App</h3> </div> </div> <div class="row text-center"> <img src="/images/NewBannerBOOTS_2.png" width="400" /> </div> </div> <h2>Product Details</h2> <div> <form class="form-horizontal"> <div class="form-group"> <label class="col-sm-2 control-label">Product Id:</label> <div class="col-sm-10"> <p class="form-control-static">1</p></div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Description:</label> <div class="col-sm-10"> <p class="form-control-static">Spring Framework Guru Shirt</p> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Price:</label> <div class="col-sm-10"> <p class="form-control-static">18.95</p> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Image Url:</label> <div class="col-sm-10"> <p class="form-control-static">http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg</p> </div> </div> </form> </div> </div> </body> </html> Forwarded URL = null Redirected URL = null Cookies = []
Summary
The new @WebMVC
used with MockBean
allows creating powerful yet simple tests for your Spring MVC apps. Unlike the @SpringBootTest
annotation, the @WebMvcTest
annotation disables full auto-configuration. @WebMvcTest
only auto-configures the Spring MVC infrastructure and limits scanned beans to @Controller
, @ControllerAdvice
, @JsonComponent
, Filter
, WebMvcConfigurer
, and HandlerMethodArgumentResolver
beans.
When you use @WebMvcTest
, regular @Component
, @Service
, or @Repository
beans will not be scanned – an important point to differentiate @WebMvcTest
from a full-blown @SpringBootTest
.
If you’re looking to load your full application configuration and use MockMVC
, you should consider @SpringBootTest
combined with @AutoConfigureMockMvc
rather than @WebMvcTest
. I will cover it in an upcoming post of this Spring MVC testing series. I will also help you to explore more about mocking services and JPA repositories with @MockBean
combined with @DataJpaTest
and @WebMvcTest
, and also how to unit test RESTful controller’s GETs and POSTs using MockMvc
and @JsonTest
.
dvega
Really great article John! Looking forward to the next installment of this series 🙂
jt
Thanks Dan!
Mario Kusek
Nice post. Thanks!
What I have trouble testing is controller method that has argument UserDetails. I always get null value. Can you write post about that?
RizziCR
Nice Article.
The Link for “Spring Boot Web Application – Part 3 – Spring Data JPA” at the first paragraph is not working/linked to “about:blank”
mohdalihpk
It is great article. I never think spring boot like this. Can you write an article on spring boot application split in multiple jars? Let me explain, I am using entities in two applications but the entities are exactly same, how can I make a jar and distribute in both projects. The jars also use spring capabilities.
Looking forward for your reply.
jt
Glad you liked it. I don’t have an example handy – but what you’re asking about is a multi-module project – which is easy to do in Maven or Gradle.