Friday 6 December 2013

Unit Testing of Spring Web MVC Application using MockMvc

This is a simple post which will guide you to perform complete end to end integration testing of a Spring Web Application and not just the controllers using Spring Web MVC Test Framework. MockMvc class is used to mock the MVC pattern. Here is a simple example :

If you are using Maven following are the required dependencies:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>4.1.0.Final</version>
</dependency>

If the database that you are using is not Hibernate then you need to add the above dependency for hibernate-validator for Bean Validation

Suppose this is the Controller class for which testing is to be done

LoginController.java :

@Controller
public class LoginController {

@Autowired
ILoginService loginService;

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login() {
  LOG.debug("Request to get the login page");
  TestUser user = new TestUser();
  return new ModelAndView("login", "user", user);
}

@RequestMapping(value = "login", method = RequestMethod.POST)
public String login(@ModelAttribute TestUser user, Model model){
  
  boolean isValid = false;

  isValid = loginService.validateUser(user);

  if(isValid)
      model.addAttribute("result","valid");
  else 
      model.addAttribute("result","invalid");

  return "next";
}

Model TestUser.java:


public class TestUser {
  
   private String userName;
   private String password;
 
   public String getUserName() {
        return userName;
   }

   public void setUserName(String userName) {
 this.userName = userName;
   }

   public String getPassword() {
 return password;
   }

   public void setPassword(String password) {
 this.password = password;
   }
}

As you can see loginService does the validation for the user. The Service class may internally make a connection to the database and then return the appropriate result. This complete flow can be tested if the Web Application Context is loaded for the test. This can be done simply using the following configuration:


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = "file:WebContent/WEB-INF/spring-servlet-test.xml")
public class LoginControllerTest {

 @Autowired
 private WebApplicationContext wac;

 private MockMvc mockMvc;

 @Before
 public void setup() {
  this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
 }
}
SpringJUnit4ClassRunner is a custom extension of BlockJUnit4ClassRunner which provides functionality of the Spring TestContext Framework to standard JUnit 4.5+ tests by means of the TestContextManager and associated support classes and annotations

@ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically,@ContextConfiguration declares either the application context resource locations or the annotated classes that will be used to load the context.

@WebAppConfiguration is a class-level annotation that is used to declare that the ApplicationContext loaded for an integration test should be a WebApplicationContext. The mere presence of @WebAppConfiguration on a test class ensures that a WebApplicationContext will be loaded for the test, using the default value of"file:src/main/webapp" for the path to the root of the web application (i.e., the resource base path). The resource base path is used behind the scenes to create a MockServletContext which serves as the ServletContext for the test's WebApplicationContext.

@Autowired will automatically load the cached Web Application Context. In setup(), MockMvc is injected with the resulting WebApplicationContext. MockMvc is used to perform the requests and define the expectations in the tests

Let's write the tests:


@Test
public void testGetLoginPage() throws Exception {
     mockMvc.perform(get("/login")).andExpect(status().isOk())
     .andExpect(view().name("login"))
     .andExpect(model().attributeExists("user"));
}

Here mockMvc will perform a get request for the "<contextpath>/login" url . As mentioned in the controller, a view with name 'login' and a model attribute 'user' is expected to be returned by the LoginController class

Similarly:


@Test
public void testValidLogin() throws Exception {

 // Login Request
 mockMvc.perform(post("/login").param("userName", "testUser").param("password","user123"))
               .andExpect(view().name("next"))
        .andExpect(model().attributeExists("result"));
}

Now the mockMvc will perform a post request adding the params which are required for the @ModelAttribute TestUser. After validation, the Controller class is expected to return a view named 'next' and model with an attribute 'result' .

The Spring Framework that has been used to write this blog is 3.2.0.

Note that the complete Service class can also be mocked to check the core functionality of the Controller class using Mockito. You can refer this blog written by my friend. Here's the link :

http://waheedtechblog.blogspot.in/2013/10/junit-test-with-mockito.html

1 comment:

  1. Best eCOGRA Sportsbook Review & Welcome Bonus 2021 - CA
    Looking for an eCOGRA https://febcasino.com/review/merit-casino/ Sportsbook Bonus? At this eCOGRA Sportsbook review, we're talking about a septcasino.com variety of worrione.com ECCOGRA https://deccasino.com/review/merit-casino/ sportsbook promotions.

    ReplyDelete