Spring REST实践之客户端和测试

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

 

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))); } }

 

http://www.cnblogs.com/coderland/p/5903000.html

相关文章
|
5天前
|
机器学习/深度学习 敏捷开发 人工智能
探索自动化测试的前沿技术与实践挑战
【7月更文挑战第8天】随着信息技术的飞速发展,软件测试领域正经历着前所未有的变革。自动化测试作为提升测试效率、确保软件质量的重要手段,其前沿技术与实践挑战备受关注。本文深入探讨了自动化测试的最新进展,包括人工智能在测试用例生成中的应用、持续集成/持续部署(CI/CD)流程中的自动化策略、以及云测试平台的兴起。同时,文章分析了自动化测试实施过程中遇到的主要挑战,如环境配置的复杂性、测试用例的维护问题和跨平台测试的困难,并提供了相应的解决策略。通过案例分析,展示了成功实施自动化测试的关键因素,为软件测试专业人员提供了宝贵的参考和启示。
19 2
|
1天前
|
敏捷开发 机器学习/深度学习 人工智能
探索式测试在现代软件工程中的实践与挑战
随着软件开发模式的迭代升级,传统的测试方法已不能完全满足快速变化的市场需求和敏捷开发的节奏。探索式测试作为一种灵活、启发式的测试实践,逐渐受到业界的关注。本文将深入探讨探索式测试的定义、特点及其在现代软件工程中的应用,并分析实施过程中可能遇到的挑战,旨在为软件测试人员提供一种创新的测试视角和方法。
|
5天前
|
JavaScript 前端开发 测试技术
自动化测试在API测试中的深度应用与实践
【7月更文挑战第8天】自动化测试在API测试中的应用极大地提高了测试效率和质量,为软件的快速迭代和持续交付提供了有力保障。通过合理选择测试工具、制定清晰的测试计划并遵循最佳实践,我们可以充分发挥自动化测试的优势,为软件产品的稳定性和可靠性保驾护航。
|
2天前
|
敏捷开发 Devops 测试技术
自动化测试框架的演进与实践
【7月更文挑战第11天】在软件开发的历程中,自动化测试始终扮演着不可或缺的角色。本文将通过探讨自动化测试框架的发展脉络,揭示其在现代软件工程中的应用与挑战。从早期的线性脚本到今日的模块化框架,我们将一窥自动化测试技术的演进之路,并分享实践中的经验和策略,旨在为读者提供一套实用的自动化测试解决方案。
4 1
|
7天前
|
监控 Java 测试技术
如何构建高效的自动化测试框架:策略与实践
【7月更文挑战第6天】构建高效的自动化测试框架是一个持续的过程,需要不断迭代和优化。通过遵循设计原则、选择合适的关键技术、并遵循科学的实施步骤,我们可以构建出稳定、可靠、易于维护的自动化测试框架,为软件质量的提升和交付周期的缩短提供有力支持。
|
6天前
|
监控 测试技术 持续交付
自动化测试在移动应用开发中的实践
【7月更文挑战第7天】自动化测试在移动应用开发过程中具有重要的作用。通过实施自动化测试,可以提高测试效率、保证测试质量、支持持续集成/持续部署等。然而,在实施自动化测试的过程中也会面临一些挑战,如设备兼容性、测试数据准备和维护成本等。为了克服这些挑战,我们需要采用合适的技术和策略,不断优化和完善自动化测试流程。
|
8天前
|
敏捷开发 测试技术 持续交付
自动化测试在敏捷开发中的实践
【7月更文挑战第5天】自动化测试在敏捷开发中扮演着至关重要的角色。通过制定合适的测试策略、选择合适的测试工具、编写和维护测试脚本以及集成到持续集成流程中,可以显著提高测试效率、加快反馈周期、提高测试覆盖率和降低测试成本。未来,随着技术的不断发展和敏捷开发的深入应用,自动化测试将在软件开发中发挥更加重要的作用。
|
10天前
|
敏捷开发 监控 测试技术
软件测试中的敏捷实践:如何有效整合测试与开发
【7月更文挑战第3天】在软件开发的快速迭代周期中,敏捷测试成为确保产品质量的关键。本文将探讨如何在敏捷开发环境中整合测试与开发工作,包括持续集成、测试自动化和跨功能团队协作的策略,旨在提升软件交付的速度和质量。
|
2天前
|
机器学习/深度学习 人工智能 运维
探索自动化测试的前沿技术与实践
随着软件行业的快速发展,传统的手动测试方法已难以满足日益增长的质量保证需求。自动化测试作为提高测试效率和准确性的关键手段,正逐渐成为软件开发过程中不可或缺的一部分。本文将深入探讨自动化测试的最新技术趋势,分析其在现代软件开发生命周期中的应用,并提供一系列实施策略,旨在帮助读者理解并掌握自动化测试的核心技术和方法。
|
4天前
|
Java jenkins 测试技术
Java中的自动化测试与持续集成实践
Java中的自动化测试与持续集成实践