Spring 初探(十一)(Test REST Service)

翻译 2017年01月03日 20:39:24
下面对于Test REST Service展开介绍

用到的不太熟悉的函数:
org.junit.Before
 在进行一些test之前 后续可能用到一些类似的对象 对于public method施加@Before
 使得method在@Test method之前运行
 当对于标记了@Before的类进行派生时,派生类@Before函数在基类之后运行。

org.junit.Test
 执行Test 的public method 在test 中抛出的异常 将会由JUnit报告为failure

要对RunWith有一个结构上的理解 需要对一些基本概念进行介绍
Junit 提供了tools来进行套件(suite)的运行及结果展示
运行tests 并在console中看结果 需要运行如下代码:
Ex:
org.junit.runner.JUnitCore.runClasses(TestClass1.class,...)

或者:
在test class中添加如下静态代码:
Ex:

public static Test suite(){
	return new JUnit4TestAdapter("YourJUnit4TestClass".class);
}

上述方法的运行是通过test runner进行的。
junit.textui.TestRunner
 这是一个通过命令行运行test的工具类。
 其以TestCase的名称为参数进行运行。
 调用进行运行的方法除了在命令行外 还可以调用runner的与run相关的方法,
 如上面的runClasses方法

Spring 提供的SpringJUnit4ClassRunner与常用的Suite
都作为自定义runner 当需要使用这些runners时需要在相应的Test class
前面加@RunWith annotation 并制定使用的runner名称。

其它第三方runner及关于runner的简要介绍参见:
https://github.com/junit-team/junit4/wiki/Test-runners
比较详尽的介绍参见:
http://www.mscharhag.com/java/understanding-junits-runner-architecture

org.springframework.boot.test.SpringApplicationConfiguration
 类一级别的annotation 可以用来指定初始化的ApplicationContext
 用于将相应的应用环境导入到测试类中。

org.springframework.http.MediaType
 可以完成关于传输内容的描述。(如格式及编码)
 具体使用方法见下例

org.springframework.http.HttpMessageConverter(接口)
 converter from and to HTTP requests and responses

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
 上述HttpMassageConverter的实现 并且可以读写json (使用ObjectMapper进行转换)
 其一般支持的数据形式设定为application/json and application/*+json with UTF-8 character set
 前面的数据格式及后面的字符集格式可以由前述MediaType进行描述。(见下例)

org.springframework.mock.http.MockHttpOutputMessage
 HttpOutputMessage 的mock 实现(用于测试的虚拟实现)
 HttpOutputMessage 仅仅是提供getBody的接口 一般被Http request在客户端实现
 或者response在服务端实现。

 其代码见:
 https://github.com/spring-projects/spring-test-mvc/blob/master/src/main/java/org/springframework/mock/http/MockHttpOutputMessage.java
 仅仅是实现测试功能。

org.springframework.test.context.junit4.SpringJunit4ClassRunner
 将要在下面使用的junit runner class
 使用不同runner class的一个原因是其给出的不同annotations

org.springframework.test.context.web.WebAppConfiguration
 class水平annotation 设定被test环境实现的ApplicationContext(应用配置的核心接口)
 应该为WebApplicationContext
 ApplicationContext的基本使用在前面已经做了例证
 如 Spring 初探(五)中的ConfigurableApplicationContext就作为
 其一个派生接口
 一般的如在手动配置xml 调用getBean时使用ApplicationContext进行相应操作
 如增加(自定义)ApplicationListener 时(调用addApplicationListener)
 时使用ConfigurableApplicationContext进行调用
 
 下面即将要提到的WebApplicationContext相对于ApplicationContext的特点
 是提供getServletContext method (其返回标准 Servlet API ServletContext)
 
 ServletContext 定义了Servlet与Servlet Container交互的方法。


org.springframework.test.web.servlet.MockMVC
 对于服务端Spring MVC test的 main入口
 有了这个entry 发送url请求及其检测(RequestMatcher)变得容易
 (提供一整套内置方法)


org.springframework.web.context.WebApplicationContext
 在 org.springframework.test.context.web.WebAppConfiguration已经介绍


这里还使用了部分静态导入的形式
 使用通配符将导入相应类的所有静态方法


org.hamcrest.Matchers.*
 提供了若干个测试描述的方法 这里所使用的是常用的
  is(a thin wrapper for equalTo(value))
 Hamcrest的功能进本上就是测试(assert assertEquals assertThat)
 其相关的简要介绍见:
 http://www.vogella.com/tutorials/Hamcrest/article.html#hamcrestoverview


org.junit.Assert
 这里使用AssertNotNull


下面的三个类给出了测试具体使用到的静态方法。
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
 发送请求的get post等方法为该类静态方法


org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
 其定义了如jsonPath(String expression, org.hamcrest.Matcher<T> matcher)
 这种根据expression找到相应的json部分并使用Matcher进行比较的方法
 其中的expression 为JSONPath expression
 其设计是要有一种类似于Xpath解析xml结构的解析json数据的方法
 相应介绍见:
 http://goessner.net/articles/JsonPath/
 可以参看xpath 与json的符号对照表来看。


org.springframework.test.web.servlet.setup.MockBuilders
 这里使用了其定义的函数 webAppContextSetup进行
 webApplicationContext 的应用环境设定。


下面给出使用到的例子及其构建的过程描述:
Ex:

package bookmarks;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;


/**
 * Created by admin on 2017/1/3.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class BookmarkRestControllerTest {

    private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(),
            Charset.forName("utf8"));

    private MockMvc mockMvc;

    private String userName = "bdussault";

    private HttpMessageConverter mappingJackson2HttpMessageConverter;

    private Account account;

    private List<Bookmark> bookmarkList = new ArrayList<>();

    @Autowired
    private BookmarkRepository bookmarkRepository;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Autowired
    private AccountRepository accountRepository;


    @Autowired
    void setConverters(HttpMessageConverter<?>[] converters){
        this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(hmc -> hmc instanceof MappingJackson2HttpMessageConverter)
                .findAny().orElse(null);
        assertNotNull("the JSON message converter must not be null", this.mappingJackson2HttpMessageConverter);
    }

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

        this.bookmarkRepository.deleteAllInBatch();
        this.accountRepository.deleteAllInBatch();

        this.account = accountRepository.save(new Account(userName, "password"));

        this.bookmarkList.add(bookmarkRepository.save(new Bookmark(account, "http://bookmark.com/1/" + userName, "A description")));
        this.bookmarkList.add(bookmarkRepository.save(new Bookmark(account, "http://bookmark.com/2/" + userName, "A description")));

    }

    @Test
    public void userNotFound() throws Exception
    {
        mockMvc.perform(post("/george/bookmarks/").content(this.json(new Bookmark())).
                contentType(contentType)).andExpect(status().isNotFound());
    }

    @Test
    public void readSingleBookmark() throws Exception{
        mockMvc.perform(get("/" + userName + "/bookmarks/"
                + this.bookmarkList.get(0).getId()))
                .andExpect(status().isOk())
                .andExpect(content().contentType(contentType))
                .andExpect(jsonPath("$.id", is(this.bookmarkList.get(0).getId().intValue())))
                .andExpect(jsonPath("$.uri", is("http://bookmark.com/1/" + userName)))
                .andExpect(jsonPath("$.description", is("A description")));
    }

    @Test
    public void readBookmarks() throws Exception {
        mockMvc.perform(get("/" + userName + "/bookmarks"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(contentType))
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].id", is(this.bookmarkList.get(0).getId().intValue())))
                .andExpect(jsonPath("$[0].uri", is("http://bookmark.com/1/" + userName)))
                .andExpect(jsonPath("$[0].description", is("A description")))
                .andExpect(jsonPath("$[1].id", is(this.bookmarkList.get(1).getId().intValue())))
                .andExpect(jsonPath("$[1].uri", is("http://bookmark.com/2/" + userName)))
                .andExpect(jsonPath("$[1].description", is("A description")));
    }

    @Test
    public void createBookmark() throws Exception {
        String bookmarkJson = json(new Bookmark(
                this.account, "http://spring.io", "a bookmark to the best resource for Spring news and information"));

        this.mockMvc.perform(post("/" + userName + "/bookmarks")
                .contentType(contentType)
                .content(bookmarkJson))
                .andExpect(status().isCreated());
    }

    protected String json(Object o)throws IOException{
        MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
        this.mappingJackson2HttpMessageConverter.write(o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
        return mockHttpOutputMessage.getBodyAsString();
    }



}

下面对上述构建过程进行描述:
对类进行RunWith指定测试runner


在初始化 contextType的过程中 需要指定主类型及子类型
这里使用了相应名称的函数 所谓主类型 子类型来源于 MINE媒体类型的区分
实际上 MediaType是作为MineType的派生类 所以具有相应初始化格式
一般在http传输中 有关额设定作为传输的参数进行设定
在下面一个小页面给出了一些简单列举:
http://www.cnblogs.com/wuzy/archive/2013/06/04/3118012.html
这个页面的http request headers 的相应属性为:
Content-Type:text/html; charset=utf-8
(chorme)
/前为主属性 后为子属性
在我们例子中需要的是 application/json; charset=utf-8


在前面已经对于Spring @Autowired 进行叙述
这里要补充的是 当对于进行@Autowired修饰的方法 如setter construct等方法
其参数为Collection or Map dependency type时
the container will autowired all beans matching the declared value type


这里对于setConverters的处理正是使用了此特性。
注意HttpMessageConverter<T> 就是使用T作为格式转换的类型
而 MappingJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object>
是实现java对象与message的转换
stream 的findAny方法返回一个Optional<T>对象
当stream不为空时返回包含的stream的某个Optional 否则为empty Optional
这里可能由于不在乎find的顺序使用并行stream比findFirst快。
相应介绍见:
http://stackoverflow.com/questions/25912185/java-8-stream-findany-vs-finding-a-random-element-in-the-stream


setup
 JpaRepository.deleteAllInBatch()
  delete all entities in a batch call


json
 此函数完成object向 json形式String的转换 所用的类及相应方法已经在上面
 提及。


userNotFound
 MockMvcRequestBuilders 静态方法post 生成MockHttpServletRequestBuilder
 对于此builder 调用content (这里是String参数)可以设定 request body
 perform会返回ResultActions对象 可以执行检测
 andExpect perform an expectation
 MockMvcResultMatcher status() 进行 response status assertions
 其isNotFound() assert response status code HttpStatus.NOT_FOUND
 这与输入的 userId george 是并不存在的是相对应的。
 
 到此为止已经多次用到builder函数 之前是 ServletUriComponentsBuilder
 builder的常见形式是其方法作为对于build对象的加工返回builder本身。



readSingleBookmark
 使用的get status content等与userNotFound类似
 所使用的JSONPath是简单的。



readBookmarks
 org.hamcrest.Matchers hasSize是对应collection调用size()的相应
 assertion


剩余的部分是易于理解的。
如果想进一步了解细节,可参看原文链接:
http://spring.io/guides/tutorials/bookmarks/

相关文章推荐

Spring 初探(十二)(HATEOAS REST Service)

下面对 Building a HATEOAS REST Service展开介绍 HATEOAS((Hypermedia as the Engine of Application State) 可以看做...

WCF REST service test

  • 2017-06-24 16:43
  • 14KB
  • 下载

JAVA+SPRING Rest web service

  • 2013-04-27 18:01
  • 254KB
  • 下载

基于REST的web service project添加spring框架

在web.xml中默认的JAX-RSServlet服务所对应的类完整路径是com.sun.jersey.spi.container.servlet.ServletContainer,使用这个的话是自己...

Spring 4 MVC @RestController 注解实现REST Service(带源码)

原文地址:http://websystique.com/springmvc/spring-4-mvc-rest-service-example-using-restcontroller/ 【本系列其...

利用Spring MVC搭建REST Service

之前写过一篇 利用JAX-RS快速开发RESTful 服务 今天来看下spring-mvc框架如何实现类似的功能:  一、pom.xml 1 xml version="1....

Spring boot学习笔记 实践教程https://spring.io/guides/gs/rest-service/

使用Intellij编辑器,利用maven工具,实践Springboot框架教程中的案例 HelloWorld

Create an Apex class that calls a REST endpoint and write a test class

参考网站: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_System_Jso...

Use Python to run REST API Automation Test

happiness ---xingyunpi Automation Test plays an important role in our test work. Now I will record ...

mongoDB的读书笔记(04)_【Replica】(05)_初探Replica set副本集的搭建 By Test模式

Replica set Deployment On Test Mode 为啥是测试模式 实战 先絮叨絮叨操作系统 在一台机器上创建模拟5个节点 进入非db连接模式 建立5个节点的test script...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)