当前位置: 移动技术网 > IT编程>开发语言>Java > 如何测试Spring MVC应用

如何测试Spring MVC应用

2020年10月13日  | 移动技术网IT编程  | 我要评论
spring的依赖注入使得我们的代码非常容易进行单元测试——@controller, @service,@entity等注解标注的类基本都是pojo(plain old java object),也就

spring的依赖注入使得我们的代码非常容易进行单元测试——@controller, @service@entity等注解标注的类基本都是pojo(plain old java object),也就是说很少依赖于spring容器本身的api。我们可以非常容易地使用junit或testng编写测试代码。另一方面,对于三层架构的spring web应用(controller, service, dao),使用mock活stub方法也能够更好的来测试我们的代码逻辑。例如service层代码的单元测试中,依赖的dao(或repository)对象都是根据应用测试需求mock出来的,而不需要真正去访问数据库。

spring web测试

在对spring web应用中的@controller代码进行单元测试的过程中,一般的方法是创建@controller对象,同时将它依赖的一些mock对象——例如mockhttpservletrequest, mockhttpservletresponse(都由spring-test模块提供,无需自己编写)作为@controller方法的参数。但是对于处理web请求的@controller代码来说,仅仅测试handler方法里的代码是远远不够的,对于一个处理http请求的@controller`,我们还需要测试:

  • @requestmapping路由是否正确
  • 数据绑定、类型转换、校验逻辑是否正确——数据包括url参数、表单、@pathvariable
  • @initbinder, @modelattribute, @exceptionhandler等注解的方法或属性计算过程

上述过程贯穿于http请求处理的生命周期中,所以对于spring web应用中@controller代码单元测试的概念,应该做一些扩充——不仅仅局限于代码本身,也要结合mvc框架中的各个处理过程。

本文接下来的内容代码,都以spring boot为例,首先假设我们通过spring boot创建了一个最简单的web mvc应用——包含了一个最简单的conroller,处理/users/{id}对应的http请求,返回值是id={id}(通过string.format()方法),那么可以为它创建如下测试代码:

import static org.springframework.test.web.servlet.request.mockmvcrequestbuilders.get;
import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.content;
import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.status;

@runwith(springjunit4classrunner.class)
@springapplicationconfiguration(classes = springmvctestdemoapplication.class)
@webappconfiguration
public class springmvctestdemoapplicationtests {

  private mockmvc mockmvc;

  @before
  public void init() {
    this.mockmvc = mockmvcbuilders.standalonesetup(new usercontroller()).build();
  }

  @test
  public void getuserbyid() throws exception {
    long id = 1;
    this.mockmvc.perform(get("/users/" + id))
        .andexpect(status().isok())
        .andexpect(content().string("id=" + id));
  }

}

运行上述测试时,很容易从控制台中的日志发现,springjunit4classrunner创建了一个spring web应用上下文,并且在其中进行了web mvc框架的配置——这里是注册@requestmapping方法。接下来mockmvc.perform()方法实际上向该spring web应用发起了一个http请求:

  • 请求的url为/users/{id}
  • andexpect()方法也就是测试中常用的assert
  • status()用于检查返回状态吗,这里是200
  • content()用于检查内容

如果我们不小心将@requestmapping的路由路径写错,那么这里运行的结果一定不会是status().isok(),这也就完成了对http请求路由的测试。接下来我们将继续探索mvc框架中的其他方面。

mock service

在spring web应用三层结构里,controller层代码通常会调用service层代码,例如:

@restcontroller
public class usercontroller {

  @autowired
  private userservice userservice;

  @requestmapping(value = "/users/{id}", method = get)
  public string get(@pathvariable("id") long id) {
    string username = userservice.getusername(id);
    return string.format("username=%s", username);
  }
}

usercontroller进行单元测试需要排除service代码的影响,所以需要对service进行mock,这里我们使用mockito框架,在spring上下文中mock一个userservice对象:

@configuration
public class testcontext {

  @bean
  public userservice userservicemock() {
    return mockito.mock(userservice.class);
  }
}

同时通过mockito的api来mockuserservice.getusername(long id)方法,@controller的测试代码如下:

@runwith(springjunit4classrunner.class)
@springapplicationconfiguration(classes = {
    springmvctestdemoapplication.class,
    testcontext.class
})
@webappconfiguration
public class springmvctestdemoapplicationtests {

  @autowired
  userservice userservice;

  @autowired
  usercontroller controller;

  mockmvc mockmvc;

  @before
  public void init() {
    this.mockmvc = mockmvcbuilders.standalonesetup(controller).build();
  }

  @test
  public void getuserbyid() throws exception {
    long id = 1l;
    string ricky = "ricky";
    mockito.when(userservice.getusername(id)).thenreturn(ricky);
    this.mockmvc.perform(get("/users/" + id))
        .andexpect(status().isok())
        .andexpect(content().string("username=" + ricky));
  }

}

由于需要进行依赖注入,所以userserviceusercontroller都使用@autowired注解。mockito.when(userservice.getusername(id)).thenreturn(ricky);表明userservice.getusername()方法的参数为1l时,返回值为"ricky",mockito提供能很多强大的mock api,更多用法请参考官方文档。

测试rest api

当我们构建rest服务时,大多数情况会使用json作为数据交换格式,spring mvc测试框架同样提供了一种简洁的方式对json结果进行断言,假设现在有@controller如下:

@requestmapping(value = "/users/{id}/json", method = get)
public user getuser(@pathvariable("id") long id) {
  string username = userservice.getusername(id);
  return new user(id, username);
}

static class user {
  public long id;
  public string username;
  //构造方法,getter/setter略
}

实际应用返回的json数据是:

{
 "id": 1,
 "username": "ricky"
}

测试代码可以这样断言:

@test
public void getuser() throws exception {
  this.mockmvc.perform(get("/users/{id}/json", id).accept(mediatype.application_json))
      .andexpect(status().isok())
      .andexpect(jsonpath("$.id").value(id.intvalue()))
      .andexpect(jsonpath("$.username").value(ricky));
}

$.id, $.username都是jsonpath提供的json表达式,可以通过jsonpathvalue()等方法来轻松对json数据进行断言而不需要自己编写json文本处理。

以上就是如何测试spring mvc应用的详细内容,更多关于测试spring mvc应用的资料请关注移动技术网其它相关文章!

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网