作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环境发现bug的成本要低于QA环境,QA环境发现bug的成本要低于Prod环境,Prod环境发现bug的成本最高,这也是每个研发人员最不愿意遇到但永远避不掉的现实。

虽然不能完全避免,但我们可以对自己的代码进行充分的测试,降低bug出现的几率。

所以, 本篇博客我们主要讲解下Spring MVC控制器的3种测试方法:

  1. 部署项目后测试
  2. 借助JUnit和Spring Test框架测试
  3. 借助SwaggerUI接口文档测试

1. 部署项目后测试

在前2篇博客中,我们采取的就是这种测试方式,即将项目打成war包,部署到Tomcat中,运行项目后, 借助浏览器或者Postman等工具对控制器进行测试。

如果是get请求,可以使用浏览器或者Postman测试。

如果是post、put、delete等请求,可以使用Postman进行测试。

有兴趣的同学,可以看下之前的2篇博客:

Spring入门(十二):Spring MVC使用讲解

Spring入门(十三):Spring MVC常用注解讲解

2. 借助Junit和Spring Test框架测试

上面的方法虽然可以进行测试,但每次都打包、部署、运行项目、测试,显然很不方便,不过强大的Spring通过Spring Test框架对集成测试提供了支持,接下来我们讲解具体的使用方法。

因为我们的Spring项目是通过maven管理的,所以它的项目结构有以下4个目录:

  1. src/main/java:项目代码
  2. src/main/resources:项目资源
  3. src/test/java:测试代码
  4. src/test/resources:测试资源(该目录默认没有生成,有需要的可以自己新建)

也就是说,我们可以将我们的测试代码放在src/test/java目录下,不过截止目前,我们还并未在该目录添加任何测试代码。

2.1 添加依赖

在添加测试代码前,我们需要在pom.xml中添加如下依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.18.RELEASE</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

也许有的同学会好奇,为啥本次添加的依赖增加了<scope>test</scope>, 它有啥作用呢?

带着这个疑问,我们编译下项目,发现原本编译正常的代码竟然编译报错了:

报错信息提示程序包org.junit不存在,可我们明明添加了该依赖啊,这是为什么呢,会不会和<scope>test</scope>有关呢?

恭喜你,猜对了,确实和<scope>test</scope>有关,如果你此时将该项移除,项目编译就不报错了(不过建议不要移除)。

这是因为,我们在之前添加测试代码时,都是放在src/main/java目录下的,现在依赖包增加了<scope>test</scope>,说明这些包的存活周期是在test周期,所以我们可以把之前的测试代码移到src/test/java目录下,如下所示:

再次编译项目,发现编译通过。

2.2 添加控制器

添加控制器前,新建DemoService如下所示:

package chapter05.service;

import org.springframework.stereotype.Service;

@Service
public class DemoService {
    public String saySomething() {
        return "hello";
    }
}

注意事项:该类添加了@Service注解。

然后,新建控制器NormalController,它里面的方法返回jsp视图:

package chapter05.controller;

import chapter05.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class NormalController {
    @Autowired
    private DemoService demoService;

    @RequestMapping("/normal")
    public String testPage(Model model) {
        model.addAttribute("msg", demoService.saySomething());
        return "page";
    }
}

接着新建控制器MyRestController,它里面的方法直接返回信息:

package chapter05.controller;

import chapter05.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyRestController {
    @Autowired
    private DemoService demoService;

    @RequestMapping(value = "/testRest", produces = "text/plain;charset=UTF-8")
    public String testRest() {
        return demoService.saySomething();
    }
}

2.3 添加测试代码

在src/test/java下新建包chapter05,然后在其下面新建测试类TestControllerIntegrationTests如下所示:

package chapter05;

import chapter05.config.MyMvcConfig;
import chapter05.service.DemoService;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MyMvcConfig.class})
@WebAppConfiguration("src/main/resources")
public class TestControllerIntegrationTests {
    private MockMvc mockMvc;

    @Autowired
    private DemoService demoService;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
    }
}

代码讲解:

@RunWith(SpringJUnit4ClassRunner.class)用于在JUnit环境下提供Spring Test框架的功能。

@ContextConfiguration(classes = {MyMvcConfig.class})用来加载配置ApplicationContext,其中classes属性用来加载配置类,MyMvcConfig配置类的代码如下所示:

package chapter05.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Spring MVC配置
 */
@Configuration
@EnableWebMvc
@ComponentScan("chapter05")
public class MyMvcConfig {
    /**
     * 视图解析器配置
     *
     * @return
     */
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);

        return viewResolver;
    }
}

@WebAppConfiguration("src/main/resources") 用来声明加载的ApplicationContext是一个WebApplicationContext,它的属性指定的是Web资源的位置,默认为src/main/webapp,这里我们修改成

src/main/resources。

MockMvc用来模拟Mvc对象,它在添加了@Before注解的setup()中,通过this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();进行初始化赋值。

然后往测试类中添加如下测试代码:

@Test
public void testNormalController() throws Exception {
    mockMvc.perform(get("/normal"))
            .andExpect(status().isOk())
            .andExpect(view().name("page"))
            .andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))
            .andExpect(model().attribute("msg", demoService.saySomething()));
}

代码解释:

perform(get("/normal"))用来模拟向/normal发起get请求,

andExpect(status().isOk())预期返回的状态码为200,

andExpect(view().name("page"))预期视图的逻辑名称为page,

andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))预期视图的真正路径是/WEB-INF/classes/views/page.jsp",

andExpect(model().attribute("msg", demoService.saySomething()))预期Model里有一个msg属性,它的值是demoService.saySomething()的返回值hello。

执行该测试方法,测试通过:

最后往测试类中添加如下测试代码:

@Test
public void testRestController() throws Exception {
    mockMvc.perform(get("/testRest"))
            .andExpect(status().isOk())
            .andExpect(content().contentType("text/plain;charset=UTF-8"))
            .andExpect(content().string(demoService.saySomething()));
}

代码解释:

perform(get("/testRest"))用来模拟向/testRest发起get请求,

andExpect(status().isOk())预期返回的状态码为200,

andExpect(content().contentType("text/plain;charset=UTF-8"))预期返回值的媒体类型为text/plain;charset=UTF-8,

andExpect(content().string(demoService.saySomething()))预期返回值的内容为demoService.saySomething()的返回值hello。

执行该测试方法,测试通过:

3. 源码及参考

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

汪云飞《Java EE开发的颠覆者:Spring Boot实战》

4. 最后

欢迎扫码关注微信公众号:「申城异乡人」,定期分享Java技术干货,让我们一起进步。

Spring入门(十四):Spring MVC控制器的2种测试方法的更多相关文章

  1. Spring学习十四----------Spring AOP实例

    © 版权声明:本文为博主原创文章,转载请注明出处 实例 1.项目结构 2.pom.xml <project xmlns="http://maven.apache.org/POM/4.0 ...

  2. Spring学习(十四)----- Spring Auto Scanning Components —— 自动扫描组件

    一.      Spring Auto Scanning Components —— 自动扫描组件 1.      Declares Components Manually——手动配置componen ...

  3. Spring 学习十四 Spring security安全

    Spring security: 我用过的安全机制:   oauth2, filter,  secured方法保护 9.2  保护web请求: 9.2.1  代理Servlet过滤器: Delegat ...

  4. Spring入门(十):Spring AOP使用讲解

    1. 什么是AOP? AOP是Aspect Oriented Programming的缩写,意思是:面向切面编程,它是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 可以认为AOP是 ...

  5. Spring Boot(十四):spring boot整合shiro-登录认证和权限管理

    Spring Boot(十四):spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉 ...

  6. Spring入门(十二):Spring MVC使用讲解

    1. Spring MVC介绍 提到MVC,参与过Web应用程序开发的同学都很熟悉,它是展现层(也可以理解成直接展现给用户的那一层)开发的一种架构模式,M全称是Model,指的是数据模型,V全称是Vi ...

  7. Spring入门(四)— 整合Struts和Hibernate

    一.Spring整合Struts 1. 初步整合 只要在项目里面体现spring和 strut即可,不做任何的优化. struts 环境搭建 创建action public class UserAct ...

  8. Spring Boot2 系列教程(二十四)Spring Boot 整合 Jpa

    Spring Boot 中的数据持久化方案前面给大伙介绍了两种了,一个是 JdbcTemplate,还有一个 MyBatis,JdbcTemplate 配置简单,使用也简单,但是功能也非常有限,MyB ...

  9. Spring Cloud(十四):Ribbon实现客户端负载均衡及其实现原理介绍

    年后到现在一直很忙,都没什么时间记录东西了,其实之前工作中积累了很多知识点,一直都堆在备忘录里,只是因为近几个月经历了一些事情,没有太多的经历来写了,但是一些重要的东西,我还是希望能坚持记录下来.正好 ...

随机推荐

  1. 【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】

    利用叉积计算多边形的面积 我们都知道计算三角形的面积时可以用两个邻边对应向量积(叉积)的绝对值的一半表示,那么同样,对于多边形,我们可以以多边形上的一个点为源点,作过该点并且过多边形其他点中的某一个的 ...

  2. H5 API编码、解码

    方式一.decodeURI 解码 encodeURI 编码 方式二. var str = 'hello'; //加密 data base 64编码 组成部分 0-9 a-z A-Z +/ = 64位个 ...

  3. MySQL数据库之单表查询中关键字的执行顺序

    目录 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 2 执行顺序 3 关键字使用语法 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 select distinct from ...

  4. 设计模式(C#)——05适配器模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       自然界有一条规则--适者生存.意思是生物要使用自然界的变化:在程序界中则需要新环境调用现存对象.那么,如何在新环境中 ...

  5. Java网络编程之InetAddress浅析

    Java网络编程之InetAddress浅析 一.InetAddress综述 IP地址是IP使用的32位(IPv4)或者128位(IPv6)位无符号数字,它是传输层协议TCP,UDP的基础.InetA ...

  6. Mysql高手系列 - 第4天:DDL常见操作汇总

    这是Mysql系列第4篇. 环境:mysql5.7.25,cmd命令中进行演示. DDL:Data Define Language数据定义语言,主要用来对数据库.表进行一些管理操作. 如:建库.删库. ...

  7. Win10安装Linux系统

    windows系统安装虚拟机,常见的是利用VMware Workstation这款软件来进行安装.在未接触Docker之前,我一直通过这款软件来进行管理的.docker是运行在linux环境下的,那怎 ...

  8. 牛客Wannafly挑战赛13-BJxc军训-费马小定理、分式取模、快速幂

    参考:https://blog.csdn.net/qq_40513946/article/details/79839320 传送门:https://www.nowcoder.com/acm/conte ...

  9. TLS加密远程连接Docker

    <Docker远程连接设置>一文讲述了开启Docker远程连接的方法,但那种方法不安全,因为任何客户端都可以通过Docker服务的IP地址连接上去,今天我们就来学习Docker官方推荐的安 ...

  10. 详细的漏洞复现:Shellshock CVE-2014-6271 CVE-2014-7169

    目录 前言 漏洞原理 利用方式 复现过程 1. 环境准备 (1) 为容器配置固定IP地址 (2) 查看bash版本 2. 本地验证:测试镜像系统是否存在漏洞 3. 远程模拟验证(原理验证) (1) 查 ...