Spring MockMvc

Spring provides MockMvc as the main entry point that is empowered with methods to start with the server-side testing. The implementation of the MockMVCBuilders interface will be used to create a MockMVC object. MockMVCBuilders provides the following static methods, which give an opportunity to get the implementation of MockMVCBuilders:

  • xmlConfigSetUp(String configLocation): This will be used when the application context is configured using XML configuration files, as shown in the following lines of code:
        MockMvcmockMVC= 
MockMvcBuilders.xmlConfigSetUp("classpath:myConfig.xml").
build();
  • annotationConfigSetUp(class configClasses): This is used when we use the Java class to configure the application context. The following code shows how to use MyConfig.java as a configuration class:
        MockMvcmockMVC=  
MockMvcBuilders.annotationConfigSetUp(MyConfiog.class).
build();
  • standaloneSetUp (object controllers): This is used when developers configure the test controllers and its required MVC components. The following code shows using MyController for the configuration:
        MockMvcmockMVC= MockMvcBuilders.standaloneSetUp( 
newMyController()).build();
  • webApplicationContextSetUp(WebApplicationContext context): This is used when the developers have already fully initialized the WebApplicationContext instance. The following code shows how to use this method:
        @Autowired 
WebApplicationContext webAppContext;

MockMvcmockMVC= MockMVCBuilders.webApplicationContextSetup(
webAppContext).build();

MockMvc has the perform() method, which accepts the instance of RequestBuilder and returns ResultActions. MockHttpServletRequestBuilder is an implementation of RequestBuilder, which has methods to build the request by setting request parameters and sessions. The following table shows the methods that facilitate building the request:

Method name The data method description
Accept This helps in setting the Accept header to the given media type
buildRequest This helps in building MockHttpServletRequest
createServletRequest This is based on ServletContext; the method creates a new MockHttpServletRequest
Param This helps in setting request parameters to MockHttpServletRequest
principal This helps in setting the principal of the request
locale This helps in setting the locale of the request
requestAttr This helps in setting a request attribute
Session, sessionAttr, sessionAttrs This helps in setting session or session attributes to the request
characterEncoding This helps in setting character encoding to the request
content and contentType This helps in setting the body and content type header of request
header and headers This helps in adding one or all headers to the request
contextPath This helps in specifying the part of requestURI that represents the context path
Cookie This helps in adding cookies to the request
flashAttr This helps in setting the input flash attribute
pathInfo This helps in specifying the part of requestURI that represents pathInfo
Secure This helps in setting a secure property of ServletRequest, such as HTTPS
servletPath This helps in specifying a part of requestURI, which represents the path to which the servlet is mapped

The perfom() method of MockMvc returns ResultActions, which facilitates the assertions of the expected result using the following methods:

Method name Description
andDo This takes a general action
andExpect This takes the expected action
annReturn This returns the result of the expected request, which can be directly accessed

Let's use MockMvc to test AddBookController developed in the ReadMyBooks application step by step:

  1. Add TestAddBookController as the JUnit test case in the com.packt.ch06.controllers.test_controllers package.
  2. Annotate the class by @WebAppConfiguration, @ContextConfiguration, and @RunWith, as we did in the earlier code.
  3. Add the data members of type WebApplicationContext and AddBookController. Annotate both by @Autowired.
  4. Add data member of type MockMvc, initialize it in the setup() method, and release memory in the teardown() method, as shown in the following piece of code:
        @WebAppConfiguration 
@ContextConfiguration(
{ "file:WebContent/WEB-INF/books-servlet.xml"})
@RunWith(value = SpringJUnit4ClassRunner.class)
public class TestAddBookController {

@Autowired
WebApplicationContext wac;

MockMvc mockMVC;

@Autowired
AddBookController addBookController;

@Before
public void setUp() throws Exception {
mockMVC=
MockMvcBuilders.standaloneSetup(addBookController).
build();
}
}
  1. Let's add the code to test the addBook() method in testAddBook() as follows:
    1. Initialize the request by setting values of the following:
      • URI on which the method is invoked
      • The content-type, as the form submission results in method invocation
      • The model attribute book with default values
      • The request parameters of the form
    2. Test the result by checking the following:
      • View name
      • Model attribute name
    3. Use andDo() to print the result of the test actions on the console.

The code for the testAddBook() method is as follows:

        @Test 
public void testAddBook() {
try {
mockMVC.perform(MockMvcRequestBuilders.post(
"/addBook.htm")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("bookName", "Book_test")
.param("author", "author_test")
.param("description", "adding book for test")
.param("ISBN", "1234")
.param("price", "9191")
.param("publication", "This is the test publication")
.requestAttr("book", new Book()))
.andExpect(MockMvcResultMatchers.view().name("display"))
.andExpect(MockMvcResultMatchers.model()
.attribute("auth_name","author_test"))
.andDo(MockMvcResultHandlers.print());
} catch (Exception e) {
// TODO: handle exception
fail(e.getMessage());
}
}

The matching of the expected behavior in andExpect()is facilitated by ResultMatcher. MockMvcResultMatcher is an implementation of ResultMatcher, which provides the methods to match the view, cookie, header, model, request, and many other parameters. The andDo() method prints MvcResult to OutputStream.

  1. Run the test case and, surprisingly, it will fail. A part of the output is as follows:

It shows the validation error even though we had given all input as per the validation rules. Which validation has failed is not clear from the output. No, there's no need to panic and check the validation one by one.

  1. Instead of creating more confusion, let's add the test code for validation using attributeHasErrors(), as shown in the following statement:
        @Test 
public void test AddBook_Form_validation() {
try {
mockMVC.perform(MockMvcRequestBuilders.post(
"/addBook.htm")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("bookName", "Book_test")
.param("author", "author_test")
.param("description", "adding book for test")
.param("ISBN", "12345")
.param("price", "9191")
.param("publication", "This is the test publication")
.requestAttr("book", new Book()))
.andExpect(MockMvcResultMatchers.view().name("bookForm"))
.andExpect(MockMvcResultMatchers.model().
attributeHasErrors("book"))
.andDo(MockMvcResultHandlers.print());
}
catch (Exception e) {
fail(e.getMessage());
e.printStackTrace();
}
}
  1. The test runs successfully, proving that the input has a validation error. We can get the field whose validation failed on the console output in the errors, as follows:
        MockHttpServletRequest: 
HTTP Method = POST
Request URI = /addBook.htm
Parameters = {bookName=[Book_test],
author=[author_test],
description=[adding book for test],
ISBN=[1234],
price=[9191],
publication=[This is the test publication]}
Headers = {
Content-Type=[application/x-www-form-urlencoded]}
Handler:
Type = com.packt.ch06.controllers.AddBookController
Method = public
org.springframework.web.servlet.ModelAndView
com.packt.ch06.controllers.AddBookController.
addBook(com.packt.ch06.beans.Book,org.
springframework.validation.BindingResult)
throws java.lang.Exception
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = bookForm
View = null
Attribute = priceList
value = [300, 350, 400, 500, 550, 600]
Attribute = book
value = Book_testadding book for test9191
errors = [Field error in object 'book' on field
'description':
rejected value [adding book for test];
codes [description.length.book.description,
description.length.description,description.
length.java.lang.String,description.length];
arguments [];
default message [Please enter description
within 40 charaters only]]
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = bookForm
Redirected URL = null
Cookies = []

Although the description has characters within the specified limit of 10 to 40, let's find out the rule to know what mistake we made in Validator2.

  1. The code in the validate method for setting validation rules for publication is as follows:
        if (book.getDescription().length() < 10 ||  
book.getDescription().length() < 40)
{
errors.rejectValue("description", "description.length",
"Please enter description within 40 charaters only");
}

Yes, we set the validation for publication length as less than 40, which led to failure. We made a mistake. Let's change the code to set the rule, as length greater than 40 will not be allowed. The updated code is shown as follows:

        if (book.getDescription().length() < 10 || 
book.getDescription().length() > 40)
{
errors.rejectValue("description", "description.length",
"Please enter description within 40 charaters only");
}
  1. Now rerun testAddController to find out what happens.
  2. The test case passes successfully. Now you have a clear understanding why we are carrying out the test cases.
  3. Let's now add the code to test field validations in testAddBook_Form_validation(), as follows:
        @Test 
public void testAddBook_Form_Field_Validation()
{
try {
mockMVC.perform(MockMvcRequestBuilders.post("/addBook.htm")
.param("bookName", "")
.param("author", "author_test")
.param("description"," no desc")
.param("ISBN", "123")
.param("price", "9191")
.param("publication", "")
.requestAttr("book", new Book()))
.andExpect(MockMvcResultMatchers.view().name("bookForm"))
.andExpect(MockMvcResultMatchers.model()
.attributeHasFieldErrors("book", "description"))
.andExpect(MockMvcResultMatchers.model()
.attributeHasFieldErrors("book", "ISBN"))
.andExpect(MockMvcResultMatchers.model()
.attributeHasFieldErrors("book", "bookName"))
.andDo(MockMvcResultHandlers.print());
}catch(Exception ex)
{
fail(ex.getMessage());
}
}
  1. Run the test case, and get the result.

The controllers and DAOs are working fine. The service layer is using DAOs, so let's conduct integration testing of the service layer. You can conduct mock object testing of the service layer as we discussed and did in the DAO layer testing. We will move on to integration testing of the service as the next phase.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.191.22