spring MVC controller 的单元测试 第一部分:配置

来源:互联网 发布:聚合数据接口怎么用呀 编辑:程序博客网 时间:2024/05/22 15:49

出处:http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers-configuration/

感谢作者!

spring MVCcontroller 的单元测试

 

第一部分:配置

Spring MVC Test ,使使我们可以通过DispatcherServlet来调用controller,进行测试

第一部分描述了controller的单元测试和配置方式

首先是需要的依赖

<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>

 

 

示例使用的controller

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.

}

 

配置application context

测试配置和产品配置分离是一件很麻烦的事,而且有时,我们会忘记将测试的修改同步到产品的配置中

 

因此,我们将context的配置分离,这样,我们就可以在测试中重用部分配置,而不用单独写测试使用的配置

 

我们的配置分离如下

l   第一个配置是 “主”相关配置

l   第二个配置是  “web层”相关配置

l   第三个配置是  “持久化”相关配置

 

配置web层

l   开启注解功能

l   指定静态资源位置

l   确保静态资源能够被容器默认servlet使用

l   是controller能够被扫描到

l   配置ExceptionResolver bean.

l   配置ViewResolver bean

web层 xml配置

<?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>

 

测试用context,主要配置controller的依赖

<?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>

 

 

配置测试类

我们有两种方式配置测试类

l   使用单独的配置。这种方式适用于应用很小。

l   基于产品使用的applicationContext 的配置。

 

单独配置步骤:

l   Annotate the test class with the @RunWithannotation and ensure that the test is executed by using theSpringJUnit4ClassRunner.

l   Annotate the class with the@ContextConfiguration annotation and ensure that the correct configurationclasses (or XML configuration files) are used. If we want to use Javaconfiguration, we have to set the configuration classes as the value of theclasses attribute. On the other hand, if we prefer XML configuration, we haveto set the configuration files as the value of the locations attribute.

l   Annotate the class with the@WebAppConfiguration annotation. This annotation ensures that the applicationcontext which is loaded for our test is a WebApplicationContext.

l   Add a MockMvc field to the test class.

l   Add a TodoService field to the test classand annotate the field with the @Autowired annotation.

l   Add a WebApplicationContext field to thetest class and annotate the field with the @Autowired annotation.

l   Add a setUp() method to the test class andannotate the method with the @Before annotation. This ensures that the methodis called before each test. This method has responsibilities: it resets theservice mock before each test and create a new MockMvc object by calling thewebAppContextSetup() method of the MockMvcBuilders class.

 

单独配置太麻烦、也不实用,就不详细介绍了

 

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;

    }

}

 

使用基于配置文件的配置

步骤如下:

l   给类添加注解 @RunWith(SpringJUnit4ClassRunner)

l   给类添加注解 @ContextConfiguration 加载配置文件或配置类

l   给类添加注解 @WebAppConfiguration。这个注解确保应用上下文作为webapplicationContext被加载

l   添加一个MockMvc属性

l   作为属性,添加需要注入的依赖,并赋给他@AutoWired注解

l   添加一个WebApplicationContext属性,并赋给他@AutoWired注解

l   添加setUp()方法,并赋给他@Before 注解。这个方法负责:reset service mock、创建一个新的MockMvc对象(通过调用MockMvcBuilders的webAppContextSetup() 方法)

importorg.junit.Before;

importorg.junit.runner.RunWith;

importorg.mockito.Mockito;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.test.context.ContextConfiguration;

importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;

importorg.springframework.test.context.web.WebAppConfiguration;

importorg.springframework.test.web.servlet.MockMvc;

importorg.springframework.test.web.servlet.setup.MockMvcBuilders;

importorg.springframework.web.context.WebApplicationContext;

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = {TestContext.class, WebAppContext.class})

//@ContextConfiguration(locations = {"classpath:testContext.xml", "classpath:exampleApplicationContext-web.xml"})

@WebAppConfiguration

publicclassWebApplicationContextTodoControllerTest {

 

    privateMockMvc mockMvc;

 

    @Autowired

    privateTodoService todoServiceMock;

 

    @Autowired

    privateWebApplicationContext webApplicationContext;

 

    @Before

    publicvoidsetUp() {

        //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);

        //因为mock是由容器管理的,而bean默认是单利,如果不重置的话,b

//ean的状态可能不是初始化状态

//(如果不对的话,欢迎指正)

        Mockito.reset(todoServiceMock);

        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();

    }

}

 

 

最佳实践:将配置分开是非常重要的,因为我们可以重用配置。

 

这里我要再次感谢原文作者的不辞辛苦

http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers-configuration/

0 0
原创粉丝点击