A Note on Unit Test Spring MVC Applications

View: 72    Dowload: 0   Comment: 0   Post by: admin   Category: Development Tools   Fields: News - Media

File has been tested

Download  a-note-on-unit-test-spring-mvc.rar (10.26 KB)

You need to Sign In to download the file a-note-on-unit-test-spring-mvc.rar
If you do not have an account then Sign up for free here

This is a note on unit test Spring MVC applications.

Background

This is a note on the unit test of Spring MVC applications. Spring provides a "spring-test" package, it simulates the routing process from a URL to an MVC controller. The test frameworks can run the code in the controllers by the URLs. The two attached examples are identical, except that one of them uses "TestNG" and the other uses "JUnit".

The Maven Dependencies

To create a Spring MVC application and perform a Spring style unit test on it, you will need the following dependencies.

<dependencies>         
        <!-- Sevlet jars for compilation, provided by Tomcat -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>
        
         <!-- Spring MVC dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.3</version>
        </dependency>
        
        <!-- Test dependencies -->      
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.0.RELEASE</version>
            <scope>test</scope>
        </dependency>
</dependencies>

Depending on your choice of TestNG or JUnit, you will also need to selectively add one of the following dependencies.

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.9.10</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

The Example MVC Application

The simple MVC application in the attached examples has only one controller that is implemented in the "TestController" class.

package com.song.web.controller;
    
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
    
import com.song.web.service.ExampleService;
    
@Controller
public class TestController {
    
    @Autowired
    private ExampleService exampleService;
    
    @ResponseBody
    @RequestMapping(value = "/example-end-point", method = RequestMethod.GET)
    public Object test() {
        return exampleService.getAMap();
    }
    
}

The "@Autowired" "ExampleService" is implemented as the following.

package com.song.web.service;
    
import java.util.HashMap;
    
import org.springframework.stereotype.Service;
    
@Service
public class ExampleService{
    public HashMap<String, String> getAMap() {
        HashMap<String, String> map = new HashMap<String, String>();        
        map.put("V", "This is from a Spring service.");
        
        return map;
    }
}

The MVC application is initialized by the "MVCInitializer" class.

package com.song.web.configuration;
    
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
    
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.support
    .AbstractAnnotationConfigDispatcherServletInitializer;
    
import com.song.web.filter.NocacheFilter;
import com.song.web.service.ExampleService;
    
public class MVCInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {
    
    @EnableWebMvc
    @ComponentScan({ "com.song.web.controller" })
    public static class MVCConfiguration implements WebMvcConfigurer {
        
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            ResourceHandlerRegistration registration =  registry.addResourceHandler("/*");
            registration.addResourceLocations("/");
        }
        
        @Bean
        @Scope(value = WebApplicationContext.SCOPE_REQUEST,
            proxyMode = ScopedProxyMode.TARGET_CLASS)
        public ExampleService exampleService() {
            return new ExampleService();
        }
        
    }
    
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { MVCConfiguration.class };
    }
    
    @Override
    public void onStartup(ServletContext servletContext)
            throws ServletException {
          FilterRegistration.Dynamic nocachefilter = servletContext
                  .addFilter("nocachefilter", new NocacheFilter());
          nocachefilter.addMappingForUrlPatterns(null, false, "/*");
          
        super.onStartup(servletContext);
    }
    
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/api/*" };
    }
    
    @Override
    protected Class<?>[] getRootConfigClasses() { return null; }

}

If you deploy and run the application, and if you issue a "GET" request to "http://localhost:8080/ut-spring-mvc-testng/api/example-end-point", you can see the following response in the POSTMAN.

Unit Test With TestNG

In order to initiate the Spring context in the unit test, you need a configuration class that implements the "WebMvcConfigurer" interface.

package com.song.web.controller;
    
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
import com.song.web.service.ExampleService;
    
@EnableWebMvc
@ComponentScan({ "com.song.web.controller" })
public class TestMVCConfiguration implements WebMvcConfigurer {
    
    @Bean
    public ExampleService exampleService() {
        return new ExampleService();
    }
}
  • The "@ComponentScan" annotation tells Spring where to find the controllers;
  • The "@Bean" function returns an instance of the "ExampleService" class. If you do not want to use the actual implementation, you can return a mock of the class for unit test purpose.

The "ControllerTest" class performs the test on the "TestController".

package com.song.web.controller;
    
import java.util.HashMap;
    
import javax.servlet.http.HttpServletResponse;
    
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
    
import com.fasterxml.jackson.databind.ObjectMapper;
    
@WebAppConfiguration
@ContextConfiguration( classes = { TestMVCConfiguration.class })
public class ControllerTest extends AbstractTestNGSpringContextTests {
    
    private MockMvc mockMvc;
    
    @Autowired
    private WebApplicationContext wac;
    
    @BeforeMethod
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    
    @Test
    public void ADummyTest() {
        MockHttpServletRequestBuilder request
            = MockMvcRequestBuilders.get("/example-end-point");
        
        try {
            ResultActions resultActions = mockMvc.perform(request);
            MvcResult result = resultActions.andReturn();
            
            MockHttpServletResponse response = result.getResponse();
            int status = response.getStatus();
            
            Assert.assertEquals(HttpServletResponse.SC_OK, status);
            
            ObjectMapper mapper = new ObjectMapper();
            HashMap<String, String> data = new HashMap<String, String>();
            mapper.readerForUpdating(data).readValue(response.getContentAsString());
            
            Assert.assertEquals(data.get("V"), "This is from a Spring service.");
            
        } catch(Exception ex) { Assert.fail("Failed - " + ex.getMessage()); }
    }
}

In order that TestNG can recognize the test as a Spring style unit test, the "ControllerTest" class needs to extend the "AbstractTestNGSpringContextTests" class.

Unit Test With JUnit

If you want to use JUnit to perform the test, you can replace "testng" with "junit" in your POM dependencies.

package com.song.web.controller;

import java.util.HashMap;

import javax.servlet.http.HttpServletResponse;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
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.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
    
import com.fasterxml.jackson.databind.ObjectMapper;
    
    
@RunWith( SpringJUnit4ClassRunner.class )
@WebAppConfiguration
@ContextConfiguration( classes = { TestMVCConfiguration.class })
public class ControllerTest {
    
    private MockMvc mockMvc;
    
    @Autowired
    private WebApplicationContext wac;
    
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    
    @Test
    public void ADummyTest() {
        MockHttpServletRequestBuilder request
            = MockMvcRequestBuilders.get("/example-end-point");
        
        try {
            ResultActions resultActions = mockMvc.perform(request);
            MvcResult result = resultActions.andReturn();
            
            MockHttpServletResponse response = result.getResponse();
            int status = response.getStatus();
            
            Assert.assertEquals(HttpServletResponse.SC_OK, status);
            
            ObjectMapper mapper = new ObjectMapper();
            HashMap<String, String> data = new HashMap<String, String>();
            mapper.readerForUpdating(data).readValue(response.getContentAsString());
            
            Assert.assertEquals("This is from a Spring service.", data.get("V"));
            
        } catch(Exception ex) { Assert.fail("Failed - " + ex.getMessage()); }
    }
}

In stead of extending the "AbstractTestNGSpringContextTests" class, you need to annotate the "ControllerTest" class by "@RunWith( SpringJUnit4ClassRunner.class )".

Points of Interest

  • This is a note on unit test Spring MVC applications;
  • I hope you like my postings and I hope this note can help you one way or the other.

A Note on Unit Test Spring MVC Applications

This is a note on unit test Spring MVC applications.

Posted on 04-04-2018 

Comment:

To comment you must be logged in members.

Files with category

 
File suggestion for you
File top downloads
Codetitle.net - library source code to share, download the file to the community
Copyright © 2015. All rights reserved. codetitle.net Develope by Vinagon .Ltd