RestTemplate

可参考spring实战来写这部分。

RestTemplate免于编写乏味的样板代码,RestTemplate定义了33个与REST资源交互的方法,涵盖了HTTP动作的各种形式,其实这些方法只有11个独立的方法,而每一个方法都由3个重载的变种。

delete():在特定的URL上对资源执行HTTP DELETE操作
exchange():在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的
execute():在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象
getForEntity():发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
getForObject():GET资源,返回的请求体将映射为一个对象
headForHeaders():发送HTTP HEAD请求,返回包含特定资源URL的HTTP头
optionsForAllow():发送HTTP OPTIONS请求,返回对特定URL的Allow头信息
postForEntity():POST数据,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到
postForLocation():POST数据,返回新资源的URL
postForObject():POST数据,返回的请求体将匹配为一个对象
put():PUT资源到特定的URL

除了TRACE,RestTemplate涵盖了所有的HTTP动作。除此之外,execute()和exchange()提供了较低层次的通用方法来使用任意的HTTP方法。

每个方法都以3种方法进行了重载:

一个使用java.net.URI作为URL格式,不支持参数化URL
一个使用String作为URL格式,并使用Map指明URL参数
一个使用String作为URL格式,并使用可变参数列表指明URL参数

GET资源

有两种执行GET请求的方法:getForObject()和getForEntity()。3个getObject()方法的签名如下:

<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

类似地,getForEntity()方法的签名如下:

<T> ResponseEntity<T> getForObject(URI url, Class<T> responseType) throws RestClientException;
<T> ResponseEntity<T> getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

除了返回类型,getForObject()方法就是getForEntity()方法的镜像。实际上,它们的工作方式大同小异。它们都执行根据URL检索资源的GET请求。它们都将资源根据responseType参数匹配为一定的类型。唯一的区别在于getForObject()只返回所请求类型的对象,而getForEntity()方法会返回请求的对象以及响应的额外信息。

public Spittle[] retrieveSpittlesForSpitter(String username) {
return new RestTemplate().getForObject("http://localhost:8080/Spitter/{spitter}/spittles",
Spittle[].class, username);
} public Spittle[] retrieveSpittlesForSpitter(String username) {
ResponseEntity<Spittle[]> reponse = new RestTemplate().getForEntity(
"http://localhost:8080/Spitter/{spitter}/spittles",
Spittle[].class, username); if(reponse.getStatusCode() == HttpStatus.NOT_MODIFIED) {
throw new NotModifiedException();
} return reponse.getBody();
}

PUT资源

void put(URI url, Object request) throws RestClientException;
void put(String url, Object request, Object... uriVairables) throws RestClientException;
void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException; public void updateSpittle(Spittle spittle) throws SpitterException {
try {
String url = "http://localhost:8080/Spitter/spittles/" + spittle.getId();
new RestTemplate().put(new URI(url), spittle);
} catch(URISyntaxException e) {
throw new SpitterUpdateException("Unable to update Spittle", e);
}
} public void updateSpittle(Spittle spittle) throws SpitterException {
restTemplate.put("http://localhost:8080/Spitter/spittles/{id}",
spittle, spittle.getId());
} public void updateSpittle(Spittle spittle) throws SpitterException {
Map<String, String> params = new HashMap<String, String>();
params.put("id", spittle.getId());
restTemplate.put("http://localhost:8080/Spitter/spittles/{id}",
spittle, params);
}

DELETE资源

void delete(String url, Object... uriVariables) throws RestClientException;
void delete(String url, Map<String, ?> uriVariables) throws RestClientException;
void delete(URI url) throws RestClientException; public void deleteSpittle(long id) {
try {
restTemplate.delete(new URI("http://localhost:8080/Spitter/spittles/" + id));
} catch(URISyntaxException e) { }
}

POST资源数据

POST请求有postForObject()和postForEntity()两种方法,和GET请求的getForObject()和getForEntity()方法类似。getForLocation()是POST请求所特有的。

<T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

上面三个方法中,第一个参数都是资源要POST到的URL,第二个参数是要发送的对象,而第三个参数是预期返回的Java类型。在URL作为String类型的两个版本中,第四个参数指定了URL变量(要么是可变参数列表,要么是一个Map)。

<T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; ResponseEntity<Spitter> response = new RestTemplate().postForEntity("http://localhost:8080/Spitter/spitters",
spitter, Spitter.class);
Spitter spitter = response.getBody();
URI url = response.getHeaders().getLocation();

postForLacation()会在POST请求的请求体中发送一个资源到服务器端,返回的不再是资源对象,而是创建资源的位置。

URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException;
URI postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;
URI postForLocation(URI url, Object request) throws RestClientException; public String postSpitter(Spitter spitter) {
RestTemplate rest = new RestTemplate();
return rest.postForLocation("http://localhost:8080/Spitter/spitters",
spitter).toString();
}

交换资源

exchange方法可以在发送个服务器端的请求中设置头信息。

<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Accept", "application/json");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers); ResponseEntity<Spitter> response = rest.exchange("http://localhost:8080/Spitter/spitters/{spitter}",
HttpMethod.GET, requestEntity, Spitter.class, spitterId);

Testing REST Services

Spring框架提供了spring-test模块,spring-test模块为JNDI,Servlet和Portlet API提供了一系列的注解,工具类和mock对象。此框架同时也提供了跨测试执行过程的缓存应用上下文功能。为了能够在非Spring Boot工程中使用spring-test模块,你需要包含如下依赖:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.1.6.RELEASE</version>
<scope>test</scope>
</dependency>

Spring Boot提供了spring-boot-starter-test,它自动在Boot应用中增加了spring-test模块,同时starter POM也包含了JUnit,Mockito和Hamcrest库:

Mockito是一款流行的mocking框架。它提供了简单的API用于创建和配置mock。
Hamcrest是一款为创建matcher提供了强大词汇的框架。matcher允许你将一个对象和期望的执行的结果联系起来。Matcher使得断言更加刻度,同时它们也产生有意义的错误信息,当断言失败时。

为了更好地理解spring-test模块,下面是测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = QuickPollApplication.class)
@WebAppConfiguration
public class ExampleTest {
}
@Before
public void setup() { }
@Test
public void testSomeThing() {}
@After
public void teardown() { }

@RunWith注解用于指定具体测试类,@ContextConfiguration用于为SpringJUnit4ClassRunner指定使用哪个XML配置文件。在上例中,@SpringApplicationConfiguration是提供了附加的Spring Boot特性的特殊的ContextConfiguration版本。@WebAppConfiguration指导Spring创建web应用上下文,即WebApplicationContext。

Unit Testing REST Controllers

Spring的依赖注入使得单元测试变得非常简单。依赖能够轻松用来模拟事先定义好的行为,因此允许我们孤立的测试代码。

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;
public class PollControllerTestMock {
@Mock
private PollRepository pollRepository; @Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
} @Test
public void testGetAllPolls() {
PollController pollController = new PollController();
ReflectionTestUtils.setField(pollController, "pollRepository", pollRepository);
when(pollRepository.findAll()).thenReturn(new ArrayList<Poll>());
ResponseEntity<Iterable<Poll>> allPollsEntity = pollController.getAllPolls();
verify(pollRepository, times(1)).findAll();
assertEquals(HttpStatus.OK, allPollsEntity.getStatusCode());
assertEquals(0, Lists.newArrayList(allPollsEntity.getBody()).size());
}
}

Spring MVC Test framework Basics

Spring MVC测试框架包含四个重要的类:MockMvc,MockMvcRequestBuilders,MockMvcResultMatchers和MockMvcBuilders。org.springframework.test.web.servlet.MockMvc类是Spring MVC测试框架的核心,它能够执行HTTP请求。它只包含了perform方法:

public ResultActions perform(RequestBuilder requestBuilder) throws java.lang.Exception

RequestBuilder提供了创建GET、POST等请求的抽象接口。为了简化请求的构建,Spring MVC框架提供了org.springframework.test.web. servlet.request.MockHttpServletRequestBuilder实现,而且在此类中提供了helper静态方法集合。

post("/test_uri")
.param("admin", "false")
.accept(MediaType.APPLICATION_JSON)
.content("{JSON_DATA}");

上例中post方法用来创建POST请求。MockMvcRequestBuilder也提供了创建get、delete和put等请求的方法。param方法属于MockHttpServletRequestBuilder类,用来为请求增加参数。MockHttpServletRequestBuilder类还提供了accept、content和header等用于向请求增加data和metadata的方法。

perform方法返回org.springframework.test.web.servlet.ResultActions对象,此对象可被用来在响应上执行断言操作。

mockMvc.perform(post("/test_uri"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string("{JSON_DATA}"));

status方法验证响应的状态值。content方法用来杨峥响应体。

MockMvcBuilders类提供了两种方式构建MockMvc对象:

webAppContextSetup:利用已初始化好的WebApplicationContext构建MockMvc。和上下文相关的配置信息会在MockMvc对象创建以前加载完成。这个技术被用于end-to-end测试。
standaloneSetup:不用加载任何spring配置构建MockMvc,为测试控制器只加载基本的MVC构件。此技术被用于单元测试。 import static org.mockito.Mockito.when;
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;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.MockMvc; @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = QuickPollApplication.class)
@ContextConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class PollControllerTest {
@InjectMocks
PollController pollController;
@Mock
private PollRepository pollRepository;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(pollController).build();
}
@Test
public void testGetAllPolls() throws Exception {
when(pollRepository.findAll()).thenReturn(new ArrayList<Poll>());
mockMvc.perform(get("/v1/polls"))
.andExpect(status().isOk())
.andExpect(content().string("[]"));
}
} import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import org.springframework.boot.test.SpringApplicationConfiguration;
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 com.apress.QuickPollApplication; @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = QuickPollApplication.class)
@WebAppConfiguration
public class PollControllerIT {
@Inject
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = webAppContextSetup(webApplicationContext).build();
} @Test
public void testGetAllPolls() throws Exception {
mockMvc.perform(get("/v1/polls"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(20)));
}
}

Spring REST实践之客户端和测试的更多相关文章

  1. spring boot 实践

    二.实践 一些说明: 项目IDE采用Intellij(主要原因在于Intellij颜值完爆Eclipse,谁叫这是一个看脸的时代) 工程依赖管理采用个人比较熟悉的Maven(事实上SpringBoot ...

  2. Spring MVC 实践 - Component

    Spring MVC 实践 标签 : Java与Web Converter Spring MVC的数据绑定并非没有任何限制, 有案例表明: Spring在如何正确绑定数据方面是杂乱无章的. 比如: S ...

  3. Spring MVC 实践 - Base

    Spring MVC 实践 标签 : Java与Web Spring Web MVC Spring-Web-MVC是一种基于请求驱动的轻量级Web-MVC设计模式框架, Spring MVC使用MVC ...

  4. Spring Kafka整合Spring Boot创建生产者客户端案例

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 创建一个kafka-producer-master的maven工程.整个项目结构如下: ...

  5. Spring Boot 整合 FastDFS 客户端

    原文地址:Spring Boot 整合 FastDFS 客户端 博客地址:http://www.extlight.com 一.前言 前两篇介绍整体上介绍了通过 Nginx 和 FastDFS 的整合来 ...

  6. Spring Boot实践——Spring Boot 2.0 新特性和发展方向

    出自:https://mp.weixin.qq.com/s/EWmuzsgHueHcSB0WH-3AQw 以Java 8 为基准 Spring Boot 2.0 要求Java 版本必须8以上, Jav ...

  7. 客户端SDK测试思路

    本文来自网易云社区 作者:万春艳 是什么 客户端SDK是为第三方开发者提供的软件开发工具包,包括SDK接口.开发文档和Demo示例等.SDK和应用之间是什么关系呢?以云信即时消息服务为例,如下图所示, ...

  8. 在Spring Boot项目中使用Spock测试框架

    本文首发于个人网站:在Spring Boot项目中使用Spock测试框架 Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目 ...

  9. Spring Boot 实践 :Spring Boot + MyBatis

    Spring Boot 实践系列,Spring Boot + MyBatis . 目的 将 MyBatis 与 Spring Boot 应用程序一起使用来访问数据库. 本次使用的Library spr ...

随机推荐

  1. (转)每天一个Linux命令(8): tar

    通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar命令就是是必不可少的一个功能强大的工具.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar命令可以为linux ...

  2. 一天一点MySQL复习——存储过程

    一.存储过程概念 使用SQL编写访问数据库的代码时,可用两种方法存储和执行这些代码,一种是在客户端存储代码,并创建向数据库服务器发送的SQL命令(或SQL语句),比如在C#.Java等客户端编程语言中 ...

  3. 锋利的jQuery读书笔记---jQuery中Ajax--序列化元素、Ajax全局事件

    serialize()方法: 作用于一个jQuery对象,它能够将DOM元素内容序列化为字符串,用于Ajax请求. <!DOCTYPE html> <html> <hea ...

  4. MSSQL常用操作及方法总结

    1.在安装Sql或sp补丁的时候系统提示之前有挂起的安装操作,要求重启的解决办法: 到注册表中找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control ...

  5. PHP include()和require()方法的区别

    本文总结了PHP的include()和require()两种包含外部文件的方法的不同之处.基本上就是,加载失败的处理方法,性能,以及使用弹性方面的不同. PHP的include()和require() ...

  6. JS判断手机访问页面,根据手机访问或者PC访问跳转

    当用户访问你网站时,如果是PC端访问,则不作处理,如果是手机或者平板访问,就跳转到自己定义的手机页面去,这个在做webapp的时候经常用到,把代码分享给大家,希望对大家有所帮助. 首先,你要在页面中引 ...

  7. C# 对Excel文档打印时的页面设置

    1.对打印页面的朝向,页宽,页高进行设置 参考源码[1] using Excel = Microsoft.Office.Interop.Excel; Excel.Application tmpExce ...

  8. Web通信之:长轮询(long-polling)(转)

    Web通信之:长轮询(long-polling) “轮询”是个耐人寻味的词,第一次看到它的时候我就直接理解为“轮流查询”了.但是看到了英文才知道这个是网络通信专业的术语.轮询,其实就是一群人在排队买东 ...

  9. OFBIZ文章

    0. OFBIZ Tutorials 1. A Tour of OFBiz 2. Entities and Services - Data Models and Business Logic in O ...

  10. UIImageView 点击放大缩小

    static CGRect oldframe; -(void)showImage:(UIImageView *)avatarImageView{ UIImage *image=avatarImageV ...