在使用 Spring boot 的过程当中发现 Spring Boot 的版本更迭很是的快,而不一样的版本的不少语法和支持都有必定的区别,当遇到一个问题去 stackoverflow 搜索的时候常常会发现不一样版本的解决方案,弄得我非常苦恼。(真是找到了用 npm 的感受,每次升级包都会出问题。每到这个时候就念到了 rails 的好,一个成熟的、稳定、合理的生态体系是多么的重要!)。因此在这里我明确的在标题里提到了我所使用的版本 1.5.3
。java
Spring 官网提供了太多的 Getting Started 好比这个或者是 Hello World 的示例。这些示例真的是太太太简单了,彻底没办法做为学习的材料(再次强调,能不能看看人家 Rails 官方的 Guide 呀),而去其余地方搜索的内容又多是早期版本(由于版本更迭快呀)的内容,把不一样的版本中的众多实践方式进行比较并拼凑在一块儿也非常浪费时间,因此我在这里就在官方的基础上座一个稍微多一点的样例,但愿这里的内容能够做为实际开发中的参考。git
注意 这里所展现的测试的例子是对 RESTful API 的测试,在先后端分离,构建微服务的今天,咱们在 Spring MVC 中作模板渲染的状况愈来愈少了了,咱们主要处理的是 JSON 数据:咱们的输入不是传统的表单数据而是 JSON,咱们的输出再也不是 HTML 而是 JSON。github
测试的重要性是老生常谈了,但实际上并非全部的团队都会在写代码的同时写测试,在看到大量的 Spring Boot 的文章和代码的时候竟然很难找到一个完整的、包含着测试的项目,真是恐怖。不过作了一些 search 以后我发现 Spring Boot 目前的测试真的是很是的简单,和 Jersey 比的话那真是好的太多了。一个基本的、纯粹的 Spring MVC 的测试长以下的样子,这里涉及多个例子,我会一点点作介绍。web
@RunWith(SpringRunner.class) // [1] public class UsersApiTest { private UserRepository userRepository; @Before public void setUp() throws Exception { userRepository = mock(UserRepository.class); MockMvc mockMvc = MockMvcBuilders .standaloneSetup(new UsersApi(userRepository)) .setControllerAdvice(new CustomizeExceptionHandler()) .build(); // [2] RestAssuredMockMvc.mockMvc(mockMvc); // [3] } @Test public void should_get_empty_user_lists_success() throws Exception { // [4] given(). when(). get("/users"). then(). statusCode(200); } @Test public void should_create_user_success() throws Exception { Map<String, Object> createUserParameter = new HashMap<String, Object>() {{ put("username", "aisensiy"); }}; given() .contentType("application/json") .body(createUserParameter) .when().post("/users") .then().statusCode(201); verify(userRepository).save(any()); } @Test public void should_get_400_error_message_with_wrong_parameter_when_create_user() throws Exception { Map<String, Object> wrongParameter = new HashMap<String, Object>() {{ put("name", "aisensiy"); }}; given() .contentType("application/json") .body(wrongParameter) .when().post("/users") .then().statusCode(400) .body("fieldErrors[0].field", equalTo("username")) // [5] .body("fieldErrors.size()", equalTo(1)); } @Test public void should_get_one_user_success() throws Exception { User user = new User(UUID.randomUUID().toString(), "aisensiy"); when(userRepository.findById(eq(user.getId()))) .thenReturn(Optional.of(user)); given() .standaloneSetup(new UserApi(userRepository)) .when().get("/users/{userId}", user.getId()) // [6] .then().statusCode(200) .body("id", equalTo(user.getId())) .body("username", equalTo(user.getUsername())) .body("links.self", endsWith("/users/" + user.getId())); } }
以上的代码包含了四个测试用例,测试内容以下:spring
GET /users
获取用户列表POST /users
用合法的参数建立一个用户,返回建立成功POST /users
用非法的参数建立一个用户,返回参数错误信息GET /users/{userId}
获取单个用户的信息下面我按照对代码中标注的点一个个作解释:npm
SpringJUnit4ClassRunner
被替换为更容易阅读的 SpringRunner
,在 stackoverflow 中会找到大量的 SpringJUnit4ClassRunner
对我这种刚接触的人来讲真是带来了不少的困惑。另外,咱们在这里并无使用一个 SpringBootTest
的注解,SpringBootTest 是只有须要一个比较完整的 Spring Boot 环境的时候(好比须要作集成测试,启动 EmbeddedWebApplicationContext
的时候)须要。而咱们这里仅仅经过单元测试就能够完成任务了,这样的好处是能够大大提高测试的速度。MockMvcBuilders
是 Spring MVC 提供的一个 mock 环境,使咱们能够不启动 HTTP server 就能进行测试。这里咱们经过 standaloneSetup
的方法建立咱们要测试的 UsersApi
而且经过 setControllerAdvice
添加错误处理的机制。有关 ControllerAdvice
作异常处理的内容咱们会在后面的文章中介绍。build.gradle
引入了 rest assured 的两个包用于 json 的测试,咱们经过这个语句将所建立的 mock mvc 提供给 rest assured。status code
,实际的项目中可能须要作更详细的 json 内容的测试body("fieldErrors[0].field", equalTo("username"))
这种直接读取 json path 的测试方式相对将 json 转化成 map 再一点点的读取字段来讲真是方便的太多,有关这种测试的其余内容详见 rest assured 官方文档PathVariable
相似大多数状况下,经过 standaloneSetup
的方式就能够对 Controller
进行有效的单元测试了,固然 MockMvcBuilders
也能够引入外部的 ControllerAdvice
对错误处理进行测试。加上 rest assured 测试 json api 真是简单了太多了。不过这里并无覆盖 filter 的测试,后面的有关安全的文章会补上。json
最后附上项目所使用的 build.gradle
,完整的项目内容能够在 Github 找到。后端
// build.gradle buildscript { ext { springBootVersion = '1.5.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.flywaydb:flyway-core') compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.0') compile("org.springframework.boot:spring-boot-starter-hateoas") compile('org.springframework.boot:spring-boot-starter-web') runtime('com.h2database:h2') compileOnly('org.projectlombok:lombok') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.mybatis.spring.boot:mybatis-spring-boot-starter-test:1.3.0') testCompile 'io.rest-assured:rest-assured:3.0.2' testCompile 'io.rest-assured:spring-mock-mvc:3.0.2' }
更多信息见 aisensiy.github.ioapi