作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的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. 《C# 7.0核心技术指南》到货

    前几天有大佬推荐本书,并且折扣相当的划算,随入手一本.

  2. C++函数中,两个自动释放内存的动态内存申请类

    最近做一个事情,实现一个流程交互,其中主交互流程函数中,涉及较多的内存申请, 而健康的函数,都是在函数退出前将手动申请不再需要的内存释放掉, 使用很多方法,都避免不了较多的出错分支时,一堆的if fr ...

  3. Springboot + Stopping service [Tomcat]+ Process finished with exit code 0

    在Springboot 的版本为: <version>1.5.10.RELEASE</version> 原因:代码中有非法格式的结构,及代码写错啦,例如: <result ...

  4. 单元测试之NUnit三

    NUnit 分三篇文章介绍,入门者可阅读文章,有基础者直接参考官方文档.初次写博客,望大家指点. 导航: 单元测试之NUnit一 单元测试之NUnit二 单元测试之NUnit三 除了Assert断言外 ...

  5. Liunx软件安装之MySQL

    一.安装MySQL 1.1 配置 yum 源 centos 默认没有 MySQL 的 yum 源,所以需要先配置 yum 源. 1) 前往 官网,选择对应系统版本 2) 右键复制链接 3) 在 cen ...

  6. Unity进阶:用AssetBundle和Json做了一个玩家登陆界面

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  7. 安排:《蚂蚁花呗1234面:Redis+分布式架构+MySQL+linux+红黑树》

    前言: 大厂面试机会难得,为了提高面试通关率,建议朋友们在面试前先复盘自己的知识栈,依据掌握程度划分重要.优先级,系统地去学习!如果不准备充分就去参加面试,既会失去进入大厂的机会,更是对自己的不负责. ...

  8. Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)

    Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...

  9. SPSS数据分析方法不知道如何选择

      一提到数学,高等数学,线性代数,概率论与数理统计,数值分析,空间解析几何这些数学课程,头疼呀.作为文科生,遇见这些课程时,通常都是各种寻求帮助,班上有位宅男数学很厉害,各种被女生‘围观’,这数学为 ...

  10. 2019 HZNU Winter Training Day 13 Comprehensive Training

    A.Jongmah   CodeForces-1110D 题意:你在玩一个数字游戏,有一堆写在瓦片上的数字,希望你能组成最多的三元组(三个数字相同,或顺子). 这题用到的方法是动态规划.f[i][j] ...