欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring入门(十四):Spring MVC控制器的2种测试方法

程序员文章站 2022-10-10 21:26:41
作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环境发现bug的成本要低于QA环境,QA环境发现bug的成本要低于Prod环境,Prod环境发现bug ......

作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的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:测试资源(该目录默认没有生成,有需要的可以自己新建)

Spring入门(十四):Spring MVC控制器的2种测试方法

也就是说,我们可以将我们的测试代码放在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>, 它有啥作用呢?

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

Spring入门(十四):Spring MVC控制器的2种测试方法

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

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

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

Spring入门(十四):Spring MVC控制器的2种测试方法

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

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。

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

Spring入门(十四):Spring MVC控制器的2种测试方法

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

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

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

Spring入门(十四):Spring MVC控制器的2种测试方法

3. 源码及参考

源码地址:,欢迎下载。

craig walls 《spring实战(第4版)》

汪云飞《java ee开发的颠覆者:spring boot实战》

4. 最后

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

Spring入门(十四):Spring MVC控制器的2种测试方法