└── 2016-12-12-【接口测试】rest-assured用户手册中文版.markdown
/2016-12-12-【接口测试】rest-assured用户手册中文版.markdown:
--------------------------------------------------------------------------------
1 | >原文:https://github.com/rest-assured/rest-assured/wiki/Usage
2 | >本文github地址:https://github.com/RookieTester/rest-assured-doc
3 |
4 | 注意,如果您正在使用1.9.0或者更早的版本请参考[旧文档](https://github.com/rest-assured/rest-assured/wiki/Usage_Legacy)。
5 |
6 | REST Assured是一个可以简化HTTP Builder顶层 基于REST服务的测试过程的Java **DSL**(针对某一领域,具有受限表达性的一种计算机程序设计语言)。它支持发起POST,GET,PUT,DELETE,OPTIONS,PATCH和HEAD请求,并且可以用来验证和校对这些请求的响应信息。
7 |
8 | # 目录
9 | 1. [静态导入方法](#静态导入方法)
10 | 1. [示例](#示例)
11 | 1. [JSON 示例](#例一-JSON)
12 | 1. [JSON Schema Validation](#JSONSchemavalidation)
13 | 1. [XML 示例](#例2-XML)
14 | 1. [高级用法](#例3-复杂的解析和验证)
15 | 1. [XML](#XML示例)
16 | 2. [JSON](#JSON示例)
17 | 1. [其它示例](#其它例子)
18 | 1. [关于float和double](#关于float和double)
19 | 1. [语法关注点](#语法关注点) ([语法糖](#语法糖))
20 | 1. [获得响应体信息](#获得响应体信息)
21 | 1. [从已验证的响应体中提取值](#从已验证的响应体中提取值)
22 | 1. [JSON (使用 JsonPath)](#JSON(使用JsonPath))
23 | 1. [XML (使用XmlPath)](#XML(使用XmlPath))
24 | 1. [获取某个路径下的值](#获取某个路径下的值)
25 | 1. [Headers, cookies, status等](#headers-cookies-status-etc)
26 | 1. [获取全部header值](#multi-value-headers)
27 | 1. [获取全部cookie值](#multi-value-cookies)
28 | 1. [获取详细的cookie值](#detailed-cookies)
29 | 1. [获得响应信息](#getting-response-data)
30 | 1. [在验证响应之后提取特定的值](#extracting-values-from-the-response-after-validation)
31 | 1. [JSON (使用JsonPath)](#json-using-jsonpath)
32 | 1. [XML (使用XmlPath)](#xml-using-xmlpath)
33 | 1. [单独使用路径](#single-path)
34 | 1. [Headers, cookies, status等](#headers-cookies-status-etc)
35 | 1. [获取header](#多个header)
36 | 1. [获取cookie](#多个cookie)
37 | 1. [获取详细的cookie值](#详细的Cookies信息)
38 | 1. [指定请求数据](#指定请求数据)
39 | 1. [请求HTTP资源](#请求HTTP资源)
40 | 1. [参数化](#参数化)
41 | 1. [多值参数](#多值参数)
42 | 1. [参数不赋值](#无值参数)
43 | 1. [路径参数](#路径参数)
44 | 1. [Cookie](#Cookie)
45 | 1. [Header](#Header)
46 | 1. [Content-Type](#ContentType)
47 | 1. [请求正文](#请求正文)
48 | 1. [验证响应信息](#验证响应信息)
49 | 1. [响应体](#响应体)
50 | 1. [Cookie](#Cookie)
51 | 1. [状态码](#状态码)
52 | 1. [Header](#Header)
53 | 1. [Content-Type](#Content-Type)
54 | 1. [内容全匹配](#内容全匹配)
55 | 1. [关联类型验证](#关联类型验证)
56 | 1. [计算响应时间](#计算响应时间)
57 | 1. [认证](#认证)
58 | 1. [基本认证](#基本认证)
59 | 1. [抢占式的基本认证](#抢占式的基本认证)
60 | 1. [受质询的基本认证](#受质询的基本认证)
61 | 1. [摘要认证](#摘要认证)
62 | 1. [表单认证](#表单认证)
63 | 1. [CSRF](#csrf)
64 | 1. [OAuth](#oauth)
65 | 1. [OAuth1](#oauth-1)
66 | 1. [OAuth2](#oauth-2)
67 | 1. [Multi-part类型的表单数据](#Multi-part表单数据)
68 | 1. [对象映射](#对象映射)
69 | 1. [序列化](#序列化)
70 | 1. [基于Content-Type的序列化](#基于Content-Type的序列化)
71 | 1. [由HashMap创建JSON](#由HashMap创建JSON)
72 | 1. [使用显式序列化器](#使用显式序列化器)
73 | 1. [反序列化](#反序列化)
74 | 1. [基于Content-Type的反序列化](#content-type-based-deserialization)
75 | 1. [自定义content-type的反序列化](#自定义content-type的反序列化)
76 | 1. [使用显式反序列化器](#使用显式反序列化器)
77 | 1. [配置](#配置)
78 | 1. [自定义](#自定义)
79 | 1. 解析器
80 | 1. [自定义解析器](#自定义解析器)
81 | 1. [默认解析器](#默认解析器)
82 | 1. [默认值](#默认值)
83 | 1. [模式复用](#模式复用)
84 | 1. [过滤器](#过滤器)
85 | 1. [Response Builder](#response-builder)
86 | 1. [日志](#日志)
87 | 1. [请求日志](#请求日志)
88 | 1. [响应日志](#响应日志)
89 | 1. [认证失败日志](#认证失败日志)
90 | 1. [根路径](#根路径)
91 | 1. [路径参数](#路径参数)
92 | 1. [Session支持](#Session支持)
93 | 1. [Session过滤器](#Session过滤器)
94 | 1. [SSL](#ssl)
95 | 1. [SSL无效的主机名](#SSL无效主机名)
96 | 1. [URL编码](#URL编码)
97 | 1. [代理(proxy)配置](#proxy-configuration)
98 | 1. [静态代理配置](#static-proxy-configuration)
99 | 1. [请求规范代理配置](#request-specification-proxy-configuration)
100 | 1. [详细配置](#detailed-configuration)
101 | 1. [编码配置](#encoder-config)
102 | 1. [解码配置](#decoder-config)
103 | 1. [Session配置](#session-config)
104 | 1. [重定向(Redirect) DSL](#redirect-dsl)
105 | 1. [网络连接配置](#connection-config)
106 | 1. [JSON配置](#json-config)
107 | 1. [HTTP客户端配置](#http-client-config)
108 | 1. [SSL配置](#ssl-config)
109 | 1. [参数配置](#param-config)
110 | 1. [Spring Mock Mvc模型](#spring-mock-mvc-module)
111 | 1. [Bootstrapping RestAssuredMockMvc](#bootstrapping-restassuredmockmvc)
112 | 1. [异步请求](#asynchronous-requests)
113 | 1. [添加Request Post Processors](#adding-request-post-processors)
114 | 1. [添加Result Handlers](#adding-result-handlers)
115 | 1. [使用Result匹配器](#using-result-matchers)
116 | 1. [拦截器](#interceptors)
117 | 1. [Specifications](#specifications)
118 | 1. [重置 RestAssuredMockMvc](#resetting-restassuredmockmvc)
119 | 1. [Spring MVC 身份认证](#spring-mvc-authentication)
120 | 1. [使用 Spring Security 测试](#使用 Spring Security 测试)
121 | 1. [注入一个用户](#injecting-a-user)
122 | 1. [参数相关](#note-on-parameters)
123 | 1. [Scala支持](#scala-support-module)
124 | 1. [Kotlin支持](#kotlin)
125 | 1. [更多](#more-info)
126 |
127 | ## 静态导入方法 ##
128 |
129 | 推荐大家从以下的类中静态导入方法,以提高使用rest-assured的效率。
130 |
131 | ```java
132 | io.restassured.RestAssured.*
133 | io.restassured.matcher.RestAssuredMatchers.*
134 | org.hamcrest.Matchers.*
135 | ```
136 |
137 | 如果您想使用[Json Schema](http://json-schema.org/) validation 还应该静态导入这些方法:
138 |
139 | ```java
140 | io.restassured.module.jsv.JsonSchemaValidator.*
141 | ```
142 |
143 | 更多使用方法参阅 [Json Schema Validation](#json-schema-validation) 。
144 |
145 | 如果您正在使用SpringMVC,你可以使用[spring-mock-mvc](#spring-mock-mvc-module) 模型的Rest Assured DSL来对Spring的controller层进行单元测试。为此需要从[RestAssuredMockMvc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)静态导入这些方法,而不是`io.restassured.RestAssured`:
146 |
147 | ```java
148 | io.restassured.module.mockmvc.RestAssuredMockMvc.*
149 | ```
150 | # 示例
151 |
152 | ## 例一 - JSON ##
153 | 假设某个get请求 (to http://localhost:8080/lotto) 返回JSON如下:
154 |
155 | ```javascript
156 | {
157 | "lotto":{
158 | "lottoId":5,
159 | "winning-numbers":[2,45,34,23,7,5,3],
160 | "winners":[{
161 | "winnerId":23,
162 | "numbers":[2,45,34,23,3,5]
163 | },{
164 | "winnerId":54,
165 | "numbers":[52,3,12,11,18,22]
166 | }]
167 | }
168 | }
169 | ```
170 |
171 | REST assured可以帮您轻松地进行get请求并对响应信息进行处理。举个例子,如果想要判断lottoId的值是否等于5,你可以这样做:
172 |
173 | ```java
174 | get("/lotto").then().body("lotto.lottoId", equalTo(5));
175 | ```
176 | 又或许您想要检查winnerId的取值**包括**23和54:
177 |
178 | ```java
179 | get("/lotto").then().body("lotto.winners.winnerId", hasItems(23, 54));
180 | ```
181 |
182 | 注意: `equalTo` 和 `hasItems` 是 Hamcrest matchers的方法,所以需要静态导入 `org.hamcrest.Matchers`。
183 |
184 | 注意这里的"json path"语法使用的是Groovy的GPath标注法,不要和Jayway的JsonPath语法混淆。
185 |
186 | ### 以BigDecimal返回float和double类型 ###
187 | (译者注:Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算)
188 |
189 | 您可以对rest-assured和JsonPath进行配置,使之以BigDecimal返回json里的数值类型数据,而不是float或者double。可以参考下面json文本:
190 |
191 | ```javascript
192 | {
193 |
194 | "price":12.12
195 |
196 | }
197 | ```
198 | 默认情况下您验证price字段是否等于float类型的12.12像这样:
199 |
200 | ```java
201 | get("/price").then().body("price", is(12.12f));
202 | ```
203 | 但是如果想用rest-assured的JsonConfig来配置返回的所有的json数值都为BigDecimal类型:
204 |
205 | ```java
206 | given().
207 | config(RestAssured.config().jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))).
208 | when().
209 | get("/price").
210 | then().
211 | body("price", is(new BigDecimal(12.12));
212 | ```
213 |
214 | ### JSON Schema validation ###
215 |
216 | 自从 `2.1.0` 版本rest-assured开始支持[Json Schema](http://json-schema.org/) validation. 举个例子,在classpath中放置以下的schema文件(译者注:idea的话可以放在resources目录下),products-schema.json:
217 |
218 | ```javascript
219 | {
220 | "$schema": "http://json-schema.org/draft-04/schema#",
221 | "title": "Product set",
222 | "type": "array",
223 | "items": {
224 | "title": "Product",
225 | "type": "object",
226 | "properties": {
227 | "id": {
228 | "description": "The unique identifier for a product",
229 | "type": "number"
230 | },
231 | "name": {
232 | "type": "string"
233 | },
234 | "price": {
235 | "type": "number",
236 | "minimum": 0,
237 | "exclusiveMinimum": true
238 | },
239 | "tags": {
240 | "type": "array",
241 | "items": {
242 | "type": "string"
243 | },
244 | "minItems": 1,
245 | "uniqueItems": true
246 | },
247 | "dimensions": {
248 | "type": "object",
249 | "properties": {
250 | "length": {"type": "number"},
251 | "width": {"type": "number"},
252 | "height": {"type": "number"}
253 | },
254 | "required": ["length", "width", "height"]
255 | },
256 | "warehouseLocation": {
257 | "description": "Coordinates of the warehouse with the product",
258 | "$ref": "http://json-schema.org/geo"
259 | }
260 | },
261 | "required": ["id", "name", "price"]
262 | }
263 | }
264 | ```
265 | 您可以使用这个schema验证(`/products`)这个请求是否符合规范:
266 | ```java
267 | get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));
268 | ```
269 | `matchesJsonSchemaInClasspath` 静态导入自 `io.restassured.module.jsv.JsonSchemaValidator` 并且我们推荐从这个类中静态导入所有的方法。然而为了使用它需要依赖于`json-schema-validator` module 或者从这个网页 [下载](http://dl.bintray.com/johanhaleby/generic/json-schema-validator-3.0.1-dist.zip) 它, 又或者通过maven添加下面的依赖:
270 | ```xml
271 |
272 | io.rest-assured
273 | json-schema-validator
274 | 3.0.1
275 |
276 | ```
277 |
278 | ### JSON Schema Validation 设置项 ###
279 |
280 | rest-assured的`json-schema-validator` module使用Francis Galiegue的[json-schema-validator](https://github.com/fge/json-schema-validator) (`fge`) 库来进行验证。 如果您想配置使用基础`fge`库,你可以像下面例子中:
281 |
282 | ```java
283 | // Given
284 | JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV4).freeze()).freeze();
285 |
286 | // When
287 | get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(jsonSchemaFactory));
288 | ```
289 |
290 | using方法允许您进入`jsonSchemaFactory`的实例,rest-assured在验证期间也会进行此操作。这种方式允许我们对验证进行细粒度的配置。
291 |
292 | `fge`库也允许验证状态是 `checked`或者`unchecked`(译者注:表示不懂)。默认情况,rest-assured使用`checked`验证,但是如果你想要改变这种方式,您可以提供一个matcher的[JsonSchemaValidatorSettings](http://static.javadoc.io/io.restassured/json-schema-validator/3.0.1/io/restassured/module/jsv/JsonSchemaValidatorSettings.html)实例。举个例子:
293 |
294 | ```java
295 | get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(settings().with().checkedValidation(false)));
296 | ```
297 |
298 | 这些`settings`方法静态导入自 [JsonSchemaValidatorSettings](http://static.javadoc.io/io.restassured/json-schema-validator/3.0.1/io/restassured/module/jsv/JsonSchemaValidatorSettings.html)类。
299 |
300 | ### Json Schema Validation的静态配置###
301 | 现在想象下您总是使用`unchecked`验证,并且设置默认的json schema版本为3。与其每次都在代码里进行设置,不如静态地进行定义设置。举个例子:
302 | ```java
303 | JsonSchemaValidator.settings = settings().with().jsonSchemaFactory(
304 | JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV3).freeze()).freeze()).
305 | and().with().checkedValidation(false);
306 |
307 | get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));
308 | ```
309 |
310 | 现在任意一个由[JsonSchemaValidator](http://static.javadoc.io/io.restassured/json-schema-validator/3.0.1/io/restassured/module/jsv/JsonSchemaValidatorSettings.html)导入的`matcher`都会使用`DRAFTV3`作为默认版本并且unchecked validation。
311 |
312 | 想要重置`JsonSchemaValidator`到默认设置仅仅需要调用`reset`方法:
313 |
314 | ```java
315 | JsonSchemaValidator.reset();
316 | ```
317 |
318 | ### 不使用rest-assured的Json Schema Validation ###
319 | 您也可以在不依赖rest-assured的情况下使用`json-schema-validator` module。如想要把json文本表示为`String`类型的字符串,可以这样做:
320 |
321 | ```java
322 | import org.junit.Test;
323 | import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
324 | import static org.hamcrest.MatcherAssert.assertThat;
325 |
326 | public class JsonSchemaValidatorWithoutRestAssuredTest {
327 |
328 |
329 | @Test
330 | public void validates_schema_in_classpath() {
331 | // Given
332 | String json = ... // Greeting response
333 |
334 | // Then
335 | assertThat(json, matchesJsonSchemaInClasspath("greeting-schema.json"));
336 | }
337 | }
338 | ```
339 |
340 | 更多信息请参阅[新手入门](GattingStarted)。
341 |
342 | ### 匿名式的JSON根节点验证 ###
343 |
344 | 一个JSON文本并不总是有一个命名好的根属性。这里有个验证这种JSON的例子:
345 |
346 | ```javascript
347 | [1, 2, 3]
348 | ```
349 |
350 | 一个匿名的JSON根属性可以通过使用`$`或者空字符串作为路径来识别。举个例子,通过访问`http://localhost:8080/json`这个地址可以获得一个JSON文本,我们可以使用rest-assured验证:
351 |
352 | ```java
353 | when().
354 | get("/json").
355 | then().
356 | body("$", hasItems(1, 2, 3)); // An empty string "" would work as well
357 | ```
358 |
359 | ## 例2 - XML ##
360 | XML可以一种通过简单的方式解析。假设一个POST请求`http://localhost:8080/greetXML`返回:
361 | ```xml
362 |
363 | {params("firstName")}
364 | {params("lastName")}
365 |
366 | ```
367 | 换言之,它在请求中返还了一个基于firstname和lastname请求参数的greeting节点。您可以通过rest-assured轻易地展现和解析这个例子:
368 | ```java
369 | given().
370 | parameters("firstName", "John", "lastName", "Doe").
371 | when().
372 | post("/greetXML").
373 | then().
374 | body("greeting.firstName", equalTo("John")).
375 | ```
376 | 如果您想同时解析firstname和lastname可以这样做:
377 | ```java
378 | given().
379 | parameters("firstName", "John", "lastName", "Doe").
380 | when().
381 | post("/greetXML").
382 | then().
383 | body("greeting.firstName", equalTo("John")).
384 | body("greeting.lastName", equalTo("Doe"));
385 | ```
386 | 或者稍微简短些:
387 | ```java
388 | with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body("greeting.firstName", equalTo("John"), "greeting.lastName", equalTo("Doe"));
389 | ```
390 |
391 | 看[这里](http://groovy-lang.org/processing-xml.html#_gpath) 的链接获取有关语法的更多信息(它遵循 Groovy的 [GPath](http://groovy-lang.org/processing-xml.html#_gpath) 语法).
392 |
393 | ### XML 命名空间 ###
394 | 考虑到您需要使用[io.restassured.config.XmlConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/XmlConfig.html)声明一个命名空间。举个例子,有一个位于`http://localhost:8080`的资源`namespace-example`,返回如下的XML:
395 | ```xml
396 |
397 | sudo
398 | make me a sandwich!
399 |
400 | ```
401 |
402 | 可以然后声明`http://localhost/`这个URI并且验证其响应:
403 | ```java
404 | given().
405 | config(RestAssured.config().xmlConfig(xmlConfig().declareNamespace("test", "http://localhost/"))).
406 | when().
407 | get("/namespace-example").
408 | then().
409 | body("foo.bar.text()", equalTo("sudo make me a sandwich!")).
410 | body(":foo.:bar.text()", equalTo("sudo ")).
411 | body("foo.test:bar.text()", equalTo("make me a sandwich!"));
412 | ```
413 |
414 | 这个路径语法遵循Groovy的XmlSlurper语法。注意直到2.6.0的路径语法都*不*遵循Groovy的XmlSlurper语法。请看[release notes](https://github.com/rest-assured/rest-assured/wiki/ReleaseNotes26#non-backward-compatible-changes)可以获知2.6.0之前的版本语法是怎样的。
415 |
416 | ### XPath ###
417 |
418 | 您也可以使用x-path来解析XML响应。举个例子:
419 |
420 | ```java
421 | given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body(hasXPath("/greeting/firstName", containsString("Jo")));
422 | ```
423 |
424 | 或者
425 |
426 | ```java
427 | given().parameters("firstName", "John", "lastName", "Doe").post("/greetXML").then().body(hasXPath("/greeting/firstName[text()='John']"));
428 | ```
429 |
430 | 在XPath表达式中使用命名空间,你需要在配置中启用这些选项:
431 | ```java
432 | given().
433 | config(RestAssured.config().xmlConfig(xmlConfig().with().namespaceAware(true))).
434 | when().
435 | get("/package-db-xml").
436 | then().
437 | body(hasXPath("/db:package-database", namespaceContext));
438 | ```
439 |
440 | `namespaceContext` 是一个[javax.xml.namespace.NamespaceContext](http://docs.oracle.com/javase/7/docs/api/javax/xml/namespace/NamespaceContext.html)的实例 。
441 |
442 | ### Schema和DTD ###
443 |
444 | XML响应体也可以验证为一个XML Schema (XSD)或DTD.
445 |
446 | #### XSD 例子
447 |
448 | ```java
449 | get("/carRecords").then().assertThat().body(matchesXsd(xsd));
450 | ```
451 |
452 | #### DTD 例子
453 |
454 | ```java
455 | get("/videos").then().assertThat().body(matchesDtd(dtd));
456 | ```
457 |
458 |
459 | matchesXsd
和matchesDtd
方法在Hamcrest matchers里,你可以从io.restassured.matcher.RestAssuredMatchers
460 | 导入。
461 |
462 | ## 例3 - 复杂的解析和验证 ##
463 | 这正是rest-assured闪光点所在!由于rest-assured实现了Groovy,它可以从Groovy集合的API的优点中获益。让我们从下面的Groovy例子中开始探索:
464 |
465 | ```groovy
466 | def words = ['ant', 'buffalo', 'cat', 'dinosaur']
467 | def wordsWithSizeGreaterThanFour = words.findAll { it.length() > 4 }
468 | ```
469 |
470 | 在第一行,我们简单地定义了一个包含一些单词的列表,不过第二行更加有趣。
471 | 这里我们检索了列表里的所有长度大于4的单词,通过一个叫做findAll的Groovy闭包。
472 | 这个闭包有一个内部变量`it`,代表着列表中当前的元素。
473 | 结果是一个新的列表, `wordsWithSizeGreaterThanFour`,包含`buffalo` and `dinosaur`。
474 |
475 | 这里还有一些其它的有趣的方法,我们也可以使用在Groovy集合中:
476 |
477 | * `find` – 找到第一个匹配闭包谓词(closure predicate)的元素
478 | * `collect` – 收集在集合里的每个元素都调用的闭包返回值(collect the return value of calling a closure on each item in a collection)
479 | * `sum` – 对集合里的元素进行求和
480 | * `max`/`min` – 返回集合里的最大值/最小值
481 |
482 | 所以我们如何在使用rest-assured验证XML和JSON响应时利用这些优点?
483 |
484 | ### XML示例
485 |
486 | 比方说我们有个资源`http://localhost:8080/shopping`返回如下的XML:
487 |
488 | ```xml
489 |
490 |
491 | - Chocolate
492 | - Coffee
493 |
494 |
495 | - Paper
496 | - Pens
497 |
498 |
499 | - Kathryn's Birthday
500 |
501 |
502 | ```
503 |
504 | 又比如我们想写一个测试来检验类型为groceries的category节点有Chocolate和Coffee这两个项目。在rest-assured可以这样做:
505 |
506 | ```java
507 | when().
508 | get("/shopping").
509 | then().
510 | body("shopping.category.find { it.@type == 'groceries' }.item", hasItems("Chocolate", "Coffee"));
511 | ```
512 |
513 | 这里发生了什么事?首先使用XML路径`shopping.category`获取了所有categoriy的一个列表。在这个列表中我们又调用了一个方法,`find`,来返回有`type`这个属性且该属性值为`groceries`的单个category节点。
514 |
515 | 在这个category上我们接下来继续收集所有相关联的项目(item)。
516 |
517 | 由于这里与category相关联的项目不止一个,所以会返回一个列表。接下来我们通过Hamcrest matcher的`hasItems`方法来解析它。
518 |
519 | 但是如果我们想取得一些项目(item)但又不想进行断言验证该怎么办?您可以参考[XmlPath](http://static.javadoc.io/io.restassured/xml-path/3.0.1/io/restassured/path/xml/XmlPath.html):
520 |
521 | ```java
522 | // Get the response body as a String
523 | String response = get("/shopping").asString();
524 | // And get the groceries from the response. "from" is statically imported from the XmlPath class
525 | List groceries = from(response).getList("shopping.category.find { it.@type == 'groceries' }.item");
526 | ```
527 |
528 | 如果groceries是您对这个响应里唯一的关注点,也可以使用一个[捷径](#single-path):
529 |
530 | ```java
531 | // Get the response body as a String
532 | List groceries = get("/shopping").path("shopping.category.find { it.@type == 'groceries' }.item");
533 | ```
534 |
535 | #### 深度优先搜索
536 |
537 | 实际上之前的例子我们还可以继续简化:
538 |
539 | ```java
540 | when().
541 | get("/shopping").
542 | then().
543 | body("**.find { it.@type == 'groceries' }", hasItems("Chocolate", "Coffee"));
544 | ```
545 |
546 | `**`是一种在XML文件中做深度优先搜索的捷径。
547 |
548 | 我们搜索第一个`type`属性值等于"groceries"的节点。注意我们没有在"item"这个XML路径结束。
549 |
550 | 原因是在category节点返回一个列表的项目值时,自动调用了`toString()`这个方法(译者注:这两句有啥因果关系我没搞懂)。
551 |
552 | ### JSON示例
553 |
554 | 假设`http://localhost:8080/store`返回如下的JSON:
555 |
556 | ```javascript
557 | {
558 | "store":{
559 | "book":[
560 | {
561 | "author":"Nigel Rees",
562 | "category":"reference",
563 | "price":8.95,
564 | "title":"Sayings of the Century"
565 | },
566 | {
567 | "author":"Evelyn Waugh",
568 | "category":"fiction",
569 | "price":12.99,
570 | "title":"Sword of Honour"
571 | },
572 | {
573 | "author":"Herman Melville",
574 | "category":"fiction",
575 | "isbn":"0-553-21311-3",
576 | "price":8.99,
577 | "title":"Moby Dick"
578 | },
579 | {
580 | "author":"J. R. R. Tolkien",
581 | "category":"fiction",
582 | "isbn":"0-395-19395-8",
583 | "price":22.99,
584 | "title":"The Lord of the Rings"
585 | }
586 | ]
587 | }
588 | }
589 | ```
590 |
591 | #### 例1
592 | 在本例中我们发起一个请求"/store",并且做了一个断言:搜集满足price字段值小于10的所有book数组里的title字段,得到了"Sayings of the Century"和"Moby Dick"这两个结果:
593 |
594 | ```java
595 | when().
596 | get("/store").
597 | then().
598 | body("store.book.findAll { it.price < 10 }.title", hasItems("Sayings of the Century", "Moby Dick"));
599 | ```
600 |
601 | 就像上面XML的例子,我们使用闭包获取所有price字段值低于10的book数组,并且返回相应的title字段值集合。
602 |
603 | 然后使用`hasItems`这个匹配器来断言得到我们预期的结果。使用[JsonPath](http://static.javadoc.io/io.restassured/json-path/3.0.1/io/restassured/path/json/JsonPath.html) 我们可以用下面的方法替代:
604 |
605 | ```java
606 | // Get the response body as a String
607 | String response = get("/store").asString();
608 | // And get all books with price < 10 from the response. "from" is statically imported from the JsonPath class
609 | List bookTitles = from(response).getList("store.book.findAll { it.price < 10 }.title");
610 | ```
611 |
612 | #### 例2
613 | 考虑下该如何断言所有author字段值长度总和是否大于50的结果。
614 |
615 | 这是个挺难以回答的问题,也正展示了闭包和Groovy集合的强大之处。在rest-assured里可以:
616 |
617 | ```java
618 | when().
619 | get("/store");
620 | then().
621 | body("store.book.author.collect { it.length() }.sum()", greaterThan(50));
622 | ```
623 |
624 | 首先我们通过(`store.book.author`)得到了所有的author字段值,然后使用闭包里的方法`{ it.length() }`解析这个集合。
625 |
626 | 它所做的是对列表里的每一个author字段执行一次`length()`方法,然后返回一个新的列表。在这个列表中,我们再调用`sum()`方法来求得字符长度的总和。
627 |
628 | 最终的结果是53,并且我们使用`greaterThan`匹配器的断言结果是大于50 。
629 | 但是实际上可以继续简化这种写法。可以再次参考"[words](#例3---复杂的解析)"这个例子
630 |
631 |
632 | ```groovy
633 | def words = ['ant', 'buffalo', 'cat', 'dinosaur']
634 | ```
635 |
636 | Groovy有一个便利的方法可以遍历列表中的所有元素,使用`*`来调用。举个例子:
637 |
638 |
639 | ```groovy
640 | def words = ['ant', 'buffalo', 'cat', 'dinosaur']
641 | assert [3, 6, 3, 8] == words*.length()
642 | ```
643 |
644 | Groovy返回了一个新的包含words中每个字段字符长度的列表。我们也可以把rest-assured中的这个语法用在author列表中:
645 |
646 | ```java
647 | when().
648 | get("/store");
649 | then().
650 | body("store.book.author*.length().sum()", greaterThan(50)).
651 | ```
652 |
653 | 当然我们可以使用[JsonPath](http://static.javadoc.io/io.restassured/json-path/3.0.1/io/restassured/path/json/JsonPath.html)来获取这个结果:
654 |
655 | ```java
656 | // Get the response body as a string
657 | String response = get("/store").asString();
658 | // Get the sum of all author length's as an int. "from" is again statically imported from the JsonPath class
659 | int sumOfAllAuthorLengths = from(response).getInt("store.book.author*.length().sum()");
660 | // We can also assert that the sum is equal to 53 as expected.
661 | assertThat(sumOfAllAuthorLengths, is(53));
662 | ```
663 |
664 | ## 其它例子 ##
665 | Micha Kops曾写过一篇很优秀的博客,里面包含大量示例(包括可检出的代码)。您可以[由此进入试读](http://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework/)。
666 |
667 | [Bas Dijkstra](https://www.linkedin.com/in/basdijkstra)也开展过不少关于rest-assured的开源研究和资源。你可以[由此进入试读](http://www.ontestautomation.com/open-sourcing-my-workshop-an-experiment/),如果您想试用或者作出贡献,[他的github仓库](https://github.com/basdijkstra/workshops/)里有些可以尝试的练习题。
668 |
669 |
670 | ## 关于float和double ##
671 | 浮点型数字必须和Java的基本类型"float"区分开。举个例子,如果我们看下面的JSON对象:
672 |
673 |
674 | ```javascript
675 | {
676 |
677 | "price":12.12
678 |
679 | }
680 | ```
681 |
682 | 如下的测试将会失败,因为我们在拿一个"double"在比较,而不是"float":
683 |
684 | ```java
685 | get("/price").then().assertThat().body("price", equalTo(12.12));
686 | ```
687 |
688 | 想用"float"比较的话写法应该是:
689 |
690 | ```java
691 | get("/price").then().assertThat().body("price", equalTo(12.12f));
692 | ```
693 |
694 | ## 语法关注点 ##
695 | 当阅读rest-assured的博客时,你也许会看到许多使用"given / expect / when"语法的例子,举个例子:
696 | ```java
697 | given().
698 | param("x", "y").
699 | expect().
700 | body("lotto.lottoId", equalTo(5)).
701 | when().
702 | get("/lotto");
703 | ```
704 |
705 | 这是一种“遗留语法”,这实际上是rest-assured 1.x.版本用来写测试用例的方式。然而这种运作方式令许多用户迷惑甚至恼怒。这是因为一开始没有把"given / when / then"作为主要的技术来使用。所以rest-assured得2.0版本之前差不多不支持这种类似BDD-like测试的标准用法。"given / expect / when"在2.0仍然可用但是"given / when / then"可读性更强所以在测试用例中更为推荐。然而使用"given / expect / when"还有一个好处,就是所有的期望中的错误可以在同时展示出来,这是新语法做不到的(自从预期结果放在了最后面)。这意味着如果你有多个预期结果想要检验你可以:
706 |
707 | ```java
708 | given().
709 | param("x", "y").
710 | expect().
711 | statusCode(400).
712 | body("lotto.lottoId", equalTo(6)).
713 | when().
714 | get("/lotto");
715 | ```
716 |
717 | rest-assured将同时报告状态码预期和响应体预期结果都是错的。将这些用新语法重写:
718 |
719 | ```java
720 | given().
721 | param("x", "y").
722 | when().
723 | get("/lotto").
724 | then().
725 | statusCode(400).
726 | body("lotto.lottoId", equalTo(6));
727 | ```
728 |
729 | 将会仅仅报告首个预期/断言失败的内容(比如预期状态码是400实际是200),第二个断言将不执行。您将不得不重新运行这个用例以期获取到第二个断言的结果。
730 |
731 | ### 语法糖 ###
732 | rest-assured中另一件值得注意的是,有些语法仅仅存在于语法糖中,举个例子,"and"在一行代码中使用可以增强可读性。
733 |
734 | ```java
735 | given().param("x", "y").and().header("z", "w").when().get("/something").then().assertThat().statusCode(200).and().body("x.y", equalTo("z"));
736 | ```
737 |
738 | 这等价于:
739 |
740 | ```java
741 | given().
742 | param("x", "y").
743 | header("z", "w").
744 | when().
745 | get("/something").
746 | then().
747 | statusCode(200).
748 | body("x.y", equalTo("z"));
749 | ```
750 |
751 | # 获得响应体信息 #
752 | 你也可以获得响应的内容。比方说你想通过发起一个get请求"/lotto"并获取其响应内容。你可以以多种方式:
753 | ```java
754 | InputStream stream = get("/lotto").asInputStream(); // Don't forget to close this one when you're done
755 | byte[] byteArray = get("/lotto").asByteArray();
756 | String json = get("/lotto").asString();
757 | ```
758 |
759 | ## 从已验证的响应体中提取值 ##
760 | 您可以从响应信息中提取值,或者使用`extract`方法仅仅返回response本身的一个实例。如何你想获取响应里的值,并将其作为接下来的请求内容,这会很有用。下面是一个叫做`title`的资源返回的JSON数据:
761 | ```javascript
762 | {
763 | "title" : "My Title",
764 | "_links": {
765 | "self": { "href": "/title" },
766 | "next": { "href": "/title?page=2" }
767 | }
768 | }
769 | ```
770 |
771 | 想验证内容类型是JSON格式且标题是`My Title`,但是还想要从中提取next的值并用来发起请求,下面是使用方法:
772 |
773 | ```java
774 | String nextTitleLink =
775 | given().
776 | param("param_name", "param_value").
777 | when().
778 | get("/title").
779 | then().
780 | contentType(JSON).
781 | body("title", equalTo("My Title")).
782 | extract().
783 | path("_links.next.href");
784 |
785 | get(nextTitleLink). ..
786 | ```
787 |
788 | 如果您想提取多个值,也可以考虑返回整个响应体:
789 | ```java
790 | Response response =
791 | given().
792 | param("param_name", "param_value").
793 | when().
794 | get("/title").
795 | then().
796 | contentType(JSON).
797 | body("title", equalTo("My Title")).
798 | extract().
799 | response();
800 |
801 | String nextTitleLink = response.path("_links.next.href");
802 | String headerValue = response.header("headerName");
803 | ```
804 |
805 | ## JSON (使用 JsonPath) ##
806 | 一旦我们取得了响应体,可以使用[JsonPath](http://static.javadoc.io/io.restassured/json-path/3.0.1/io/restassured/path/json/JsonPath.html)来提取相应的数据:
807 |
808 | ```java
809 | int lottoId = from(json).getInt("lotto.lottoId");
810 | List winnerIds = from(json).get("lotto.winners.winnerId");
811 | ```
812 |
813 | 或者更高效一些:
814 | ```java
815 | JsonPath jsonPath = new JsonPath(json).setRoot("lotto");
816 | int lottoId = jsonPath.getInt("lottoId");
817 | List winnerIds = jsonPath.get("winners.winnderId");
818 | ```
819 |
820 | 注意这里我们独立地使用了`JsonPath`,而没有依赖rest-assured本身的功能,看[getting started guide](https://github.com/rest-assured/rest-assured/wiki/GettingStarted) 获取更多信息。
821 |
822 | ### JsonPath 配置 ###
823 | 您可以为JsonPath配置反序列化对象(object de-serializers),举个例子:
824 | ```java
825 | JsonPath jsonPath = new JsonPath(SOME_JSON).using(new JsonPathConfig("UTF-8"));
826 | ```
827 |
828 | 也可以静态配置好JsonPath,这样所有的JsonPath实例都会共享这个配置:
829 |
830 | ```java
831 | JsonPath.config = new JsonPathConfig("UTF-8");
832 | ```
833 |
834 | 更多JsonPath的内容参照[这篇博客](http://www.jayway.com/2013/04/12/whats-new-in-rest-assured-1-8/)。
835 |
836 | 注意这里的JsonPath基于Groovy的GPath,不要和Jayway的搞混了。
837 |
838 | ## XML (使用XmlPath) ##
839 | 您也可以使用[XmlPath](http://static.javadoc.io/io.restassured/xml-path/3.0.1/io/restassured/path/xml/XmlPath.html)相应的功能:
840 |
841 | ```java
842 | String xml = post("/greetXML?firstName=John&lastName=Doe").andReturn().asString();
843 | // Now use XmlPath to get the first and last name
844 | String firstName = from(xml).get("greeting.firstName");
845 | String lastName = from(xml).get("greeting.firstName");
846 |
847 | // or a bit more efficiently:
848 | XmlPath xmlPath = new XmlPath(xml).setRoot("greeting");
849 | String firstName = xmlPath.get("firstName");
850 | String lastName = xmlPath.get("lastName");
851 | ```
852 |
853 | 注意,您可以独立于rest-assured,单独使用`XmlPath`的功能,更多信息参见[getting started guide](https://github.com/rest-assured/rest-assured/wiki/GettingStarted)。
854 |
855 | ### XmlPath配置 ###
856 | 你可以配置XmlPath的对象反序列化器和字符编码,举个例子:
857 | ```java
858 | XmlPath xmlPath = new XmlPath(SOME_XML).using(new XmlPathConfig("UTF-8"));
859 | ```
860 |
861 | 也可以静态地配置XmlPath,使得所有的实例都能共享这套配置:
862 |
863 | ```java
864 | XmlPath.config = new XmlPathConfig("UTF-8");
865 | ```
866 |
867 | 更多关于XmlPath的信息参阅[这篇博客](http://www.jayway.com/2013/04/12/whats-new-in-rest-assured-1-8/)。
868 |
869 | ## 获取某个路径下的值 ##
870 | 如您你只是想发起一个请求并返回一个路径下的值,你可以使用一个捷径:
871 | ```java
872 | int lottoId = get("/lotto").path("lotto.lottoid");
873 | ```
874 |
875 | rest-assured会基于响应体的content-type自动决定是使用JsonPath还是XmlPath。如果这个类型在rest-assured没有被定义,它将会自动到[default parser](#default-parser)中查找。你可以自行(代码指定)决定使用哪种,比如:
876 |
877 | ```java
878 | String firstName = post("/greetXML?firstName=John&lastName=Doe").andReturn().xmlPath().getString("firstName");
879 | ```
880 |
881 | `xmlPath`, `jsonPath`和`htmlPath`都是可选项。
882 |
883 | ## Headers, cookies, status等 ##
884 |
885 | 您也可以获取 header, cookie, 状态行,状态码:
886 |
887 | ```java
888 | Response response = get("/lotto");
889 |
890 | // 获取所有 headers 信息
891 | Headers allHeaders = response.getHeaders();
892 |
893 | // 获取单个 header 信息
894 | String headerName = response.getHeader("headerName");
895 |
896 | // 获取所有 cookie 键值对
897 | Map allCookies = response.getCookies();
898 |
899 | // 获取单个 cookie 信息
900 | String cookieValue = response.getCookie("cookieName");
901 |
902 | // 获取状态行信息
903 | String statusLine = response.getStatusLine();
904 |
905 | // 获取状态码信息
906 | int statusCode = response.getStatusCode();
907 | ```
908 |
909 | ## 多个 header 和 cookie ##
910 | header 和 cookie 可以包含同名的多个值。
911 |
912 | ### 多个 header ###
913 |
914 | 要获取header的所有值,您需要首先从[Response](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/Response.html)对象中获取[Headers](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/http/Headers.html) 对象。您需要首先从Response对象中获取Headers对象。您可以使用Headers.getValues(
915 | )方法返回一个具有所有header值的List列表。
916 |
917 |
918 | ### 多个 cookie ###
919 |
920 | 要获取cookie的所有值,您需要首先从[Response](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/Response.html)对象中获取[Cookie](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/http/Cookies.html)对象。您可以使用Cookie.getValues()方法获取所有值,该方法返回包含所有Cookie值的List列表。
921 |
922 |
923 | ## 详细的 Cookies 信息 ##
924 |
925 | 如果您需要获取Cookie的路径或过期日期等详细信息,您需要从REST Assured获取一个[detailed cookie](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/http/Cookie.html)。您可以使用[Response.getDetailedCookie(java.lang.String)](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/ResponseOptions.html#getDetailedCookie-java.lang.String-) 方法获取单个Cookie,包括与给定名称相关联的所有属性。
926 |
927 | 您还可以使用[Response.getDetailedCookies()](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/ResponseOptions.html#getDetailedCookies--)方法获取所有详细的响应[cookies](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/http/Cookies.html)。
928 |
929 | # 指定请求数据 #
930 |
931 | 除了指定请求参数,您还可以指定header,Cookie,正文和Content Type。
932 |
933 | ## 请求HTTP资源 ##
934 |
935 | 您通常通过调用[request specification](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/specification/RequestSpecification.html)中的“HTTP方法”执行请求。例如:
936 |
937 | ```java
938 | when().get("/x"). ..;
939 | ```
940 | 其中`get`是HTTP请求方法。
941 |
942 | 从REST Assured 3.0.0开始,您可以通过使用该方法为请求使用任何HTTP动词。
943 |
944 | ```java
945 | when().
946 | request("CONNECT", "/somewhere").
947 | then().
948 | statusCode(200);
949 | ```
950 |
951 | 这将向服务器发送“连接”请求。
952 |
953 |
954 | ## 参数化 ##
955 | 通常您可以这样指定参数:
956 |
957 |
958 | ```java
959 | given().
960 | param("param1", "value1").
961 | param("param2", "value2").
962 | when().
963 | get("/something");
964 | ```
965 |
966 | REST Assured将自动尝试基于HTTP方法确定哪个参数类型(即查询或表单参数)。在GET的情况下,查询参数将被自动使用,在POST的情况下将使用表单参数。在某些情况下,重要的是在PUT或POST中分离表单和查询参数。你可以这样使用:
967 |
968 | ```java
969 | given().
970 | formParam("formParamName", "value1").
971 | queryParam("queryParamName", "value2").
972 | when().
973 | post("/something");
974 | ```
975 |
976 | 参数也可以url上进行设置:
977 |
978 | ```java
979 | ..when().get("/name?firstName=John&lastName=Doe");
980 | ```
981 |
982 | 参数如果上传的是文件,字节数组,输入流或文本的可以参照[Multi-part类型的表单数据](#Multi-part表单数据)部分
983 |
984 |
985 | ### 多值参数 ###
986 |
987 | 多值参数是每个参数名称具有多于一个值的参数(即,每个名称的值的列表)。您可以使用var-args指定这些值:
988 |
989 | ```java
990 | given().param("myList", "value1", "value2"). ..
991 | ```
992 |
993 | 或者使用 list 列表:
994 |
995 | ```java
996 | List values = new ArrayList();
997 | values.add("value1");
998 | values.add("value2");
999 |
1000 | given().param("myList", values). ..
1001 | ```
1002 |
1003 | ### 无值参数 ###
1004 |
1005 | 您还可以指定一个没有值的请求或表单参数:
1006 |
1007 | ```java
1008 | given().param("paramName"). ..
1009 | ```
1010 |
1011 | ### 路径参数 ###
1012 |
1013 | 您还可以在请求中指定所谓的路径参数,例如
1014 | ```java
1015 | post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);
1016 | ```
1017 | 这些种类的路径参数在REST Assured中称为“未命名路径参数”,因为它们是基于索引的(`hotelId`将等于“My Hotel”,因为它是第一个占位符)。
1018 |
1019 | 您还可以使用命名路径参数:
1020 | ```java
1021 | given().
1022 | pathParam("hotelId", "My Hotel").
1023 | pathParam("roomNumber", 23).
1024 | when().
1025 | post("/reserve/{hotelId}/{roomNumber}").
1026 | then().
1027 | ..
1028 | ```
1029 |
1030 |
1031 | 路径参数使得更容易读取请求路径,且使请求路径能够在具有不同参数值的许多测试中容易地重复使用。
1032 |
1033 | 从版本2.8.0开始,您可以混合未赋值和赋值好的路径参数:
1034 |
1035 | ```java
1036 | given().
1037 | pathParam("hotelId", "My Hotel").
1038 | when().
1039 | post("/reserve/{hotelId}/{roomNumber}", 23).
1040 | then().
1041 | ..
1042 | ```
1043 | 这里 `roomNumber` 的值`My Hotel`将被替换为 `23`.
1044 |
1045 |
1046 | 注意,指定太少或太多的参数将导致错误消息。对于高级用例,您可以从[过滤器](#过滤器)添加,更改,删除(甚至冗余的路径参数)。
1047 |
1048 |
1049 | ## Cookie ##
1050 |
1051 | 通常模式下,您可以通过以下方法指定Cookie:
1052 | ```java
1053 | given().cookie("username", "John").when().get("/cookie").then().body(equalTo("username"));
1054 | ```
1055 | 也可以像这样给cookie指定多个值:
1056 |
1057 | ```java
1058 | given().cookie("cookieName", "value1", "value2"). ..
1059 | ```
1060 |
1061 | 这将创建两个cookie:cookieName = value1和cookieName = value2。
1062 |
1063 | 您还可以使用以下方式指定详细的Cookie:
1064 |
1065 | ```java
1066 | Cookie someCookie = new Cookie.Builder("some_cookie", "some_value").setSecured(true).setComment("some comment").build();
1067 | given().cookie(someCookie).when().get("/cookie").then().assertThat().body(equalTo("x"));
1068 | ```
1069 |
1070 | 或同时指定cookies:
1071 |
1072 | ```java
1073 | Cookie cookie1 = Cookie.Builder("username", "John").setComment("comment 1").build();
1074 | Cookie cookie2 = Cookie.Builder("token", 1234).setComment("comment 2").build();
1075 | Cookies cookies = new Cookies(cookie1, cookie2);
1076 | given().cookies(cookies).when().get("/cookie").then().body(equalTo("username, token"));
1077 | ```
1078 |
1079 | ## Header ##
1080 | ```java
1081 | given().header("MyHeader", "Something").and(). ..
1082 | given().headers("MyHeader", "Something", "MyOtherHeader", "SomethingElse").and(). ..
1083 | ```
1084 |
1085 | 也可以给一个headers指定多个值:
1086 | ```java
1087 | given().header("headerName", "value1", "value2"). ..
1088 | ```
1089 |
1090 | 这将创建两个header,headerName = value1和headerName = value2
1091 |
1092 | #### Header 合并/覆盖 ####
1093 |
1094 | 默认情况下,header合并可以这样:
1095 |
1096 | ```java
1097 | given().header("x", "1").header("x", "2"). ..
1098 | ```
1099 |
1100 |
1101 | 请求将包含两个标头,“x:1”和“x:2”。您可以在[HeaderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/HeaderConfig.html)的基础上进行更改。例如:
1102 |
1103 | ```java
1104 | given().
1105 | config(RestAssuredConfig.config().headerConfig(headerConfig().overwriteHeadersWithName("x"))).
1106 | header("x", "1").
1107 | header("x", "2").
1108 | when().
1109 | get("/something").
1110 | ...
1111 | ```
1112 | 这意味着只有header “x = 2”被发送到服务器
1113 |
1114 | ## Content Type ##
1115 | ```java
1116 | given().contentType(ContentType.TEXT). ..
1117 | given().contentType("application/json"). ..
1118 | ```
1119 |
1120 |
1121 | ## 请求正文 ##
1122 | ```java
1123 | given().body("some body"). .. // Works for POST, PUT and DELETE requests
1124 | given().request().body("some body"). .. // More explicit (optional)
1125 | ```
1126 |
1127 | ```java
1128 | given().body(new byte[]{42}). .. // Works for POST, PUT and DELETE
1129 | given().request().body(new byte[]{42}). .. // More explicit (optional)
1130 | ```
1131 |
1132 |
1133 | 您还可以将Java对象序列化为JSON或XML。点击[这里](#序列化)了解详情。
1134 |
1135 |
1136 | # 验证响应数据 #
1137 |
1138 | 您还可以验证状态码,状态行,Cookie,headers,内容类型和正文。
1139 |
1140 | ## 响应体 ##
1141 |
1142 | 请参阅使用示例,例如[JSON](#例一-JSON) 或 [XML](#例2-XML).
1143 |
1144 | 您还可以将响应正文映射到Java对象,单击[这里](#deserialization) 了解详细信息。
1145 |
1146 |
1147 | ## Cookie ##
1148 | ```java
1149 | get("/x").then().assertThat().cookie("cookieName", "cookieValue"). ..
1150 | get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", "cookieValue2"). ..
1151 | get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", containsString("Value2")). ..
1152 | ```
1153 |
1154 | ## 状态码 ##
1155 | ```java
1156 | get("/x").then().assertThat().statusCode(200). ..
1157 | get("/x").then().assertThat().statusLine("something"). ..
1158 | get("/x").then().assertThat().statusLine(containsString("some")). ..
1159 | ```
1160 |
1161 |
1162 | ## Header ##
1163 |
1164 | ```java
1165 | get("/x").then().assertThat().header("headerName", "headerValue"). ..
1166 | get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", "headerValue2"). ..
1167 | get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", containsString("Value2")). ..
1168 | ```
1169 |
1170 | 还可以在验证头时使用映射函数。 例如,假设您要验证“Content-Length”头部小于1000.然后,您可以使用映射函数首先将头值转换为int,然后在使用Hamcrest验证前使用“整数” 匹配器:
1171 | ```java
1172 | get("/something").then().assertThat().header("Content-Length", Integer::parseInt, lessThan(1000));
1173 | ```
1174 |
1175 | ## Content-Type ##
1176 | ```java
1177 | get("/x").then().assertThat().contentType(ContentType.JSON). ..
1178 | ```
1179 |
1180 |
1181 | ## 内容全匹配 ##
1182 |
1183 | ```java
1184 | get("/x").then().assertThat().body(equalTo("something")). ..
1185 | ```
1186 |
1187 | ## 关联类型验证 ##
1188 |
1189 | 您可以使用响应中的数据来验证响应的另一部分。 例如,从服务端返回的以下JSON:
1190 | ```javascript
1191 | { "userId" : "some-id", "href" : "http://localhost:8080/some-id" }
1192 | ```
1193 |
1194 | 您可能会注意到,“href”属性以“userId”属性的值结尾。 如果我们想验证这个,我们可以实现一个io.restassured.matcher.ResponseAwareMatcher,可以:
1195 |
1196 | ```java
1197 | get("/x").then().body("href", new ResponseAwareMatcher() {
1198 | public Matcher> matcher(Response response) {
1199 | return equalTo("http://localhost:8080/" + response.path("userId"));
1200 | }
1201 | });
1202 | ```
1203 | 如果您使用Java 8,你可以使用lambda表达式:
1204 |
1205 | ```java
1206 | get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId"));
1207 | ```
1208 |
1209 | 有一些预定义的匹配器,您可以使用在io.restassured.matcher.RestAssuredMatchers(或io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers如果使用spring-mock-mvc模块)中定义。 例如:
1210 | ```java
1211 | get("/x").then().body("href", endsWithPath("userId"));
1212 | ```
1213 | ResponseAwareMatchers也可以与另一个ResponseAwareMatcher或与Hamcrest Matcher组成。 例如:
1214 | ```java
1215 | get("/x").then().body("href", and(startsWith("http:/localhost:8080/"), endsWithPath("userId")));
1216 | ```
1217 |
1218 | `and` 方法是由`io.restassured.matcher.ResponseAwareMatcherComposer`静态导入的。
1219 |
1220 | ## 计算响应时间 ##
1221 |
1222 | 从 REST Assured 2.8.0开始支持测量响应时间,例如:
1223 |
1224 | ```java
1225 | long timeInMs = get("/lotto").time()
1226 | ```
1227 |
1228 | 或使用特定时间单位:
1229 |
1230 | ```java
1231 | long timeInSeconds = get("/lotto").timeIn(SECONDS);
1232 |
1233 | ```
1234 | 其中SECONDS只是一个标准的TimeUnit。 您还可以使用DSL验证:
1235 |
1236 | ```java
1237 | when().
1238 | get("/lotto").
1239 | then().
1240 | time(lessThan(2000L)); // Milliseconds
1241 | ```
1242 |
1243 | 或
1244 |
1245 | ```java
1246 | when().
1247 | get("/lotto").
1248 | then().
1249 | time(lessThan(2L), SECONDS);
1250 | ```
1251 |
1252 | 需要注意的是,您只能参考性地将这些测量数据与服务器请求处理时间相关联(因为响应时间将包括HTTP往返和REST Assured处理时间等,不能做到十分准确)。
1253 |
1254 |
1255 | # 认证 #
1256 |
1257 | REST assured还支持多种认证方案,例如OAuth,摘要,证书,表单和抢占式基本认证。 您可以为每个请求设置身份验证:
1258 |
1259 | ```java
1260 | given().auth().basic("username", "password"). ..
1261 | ```
1262 |
1263 | 也可以为所有请求定义身份验证:
1264 |
1265 | ```java
1266 | RestAssured.authentication = basic("username", "password");
1267 | ```
1268 | 或者您也可以使用 [specification](#模式复用).
1269 |
1270 | ## 基本认证 ##
1271 |
1272 | 有两种类型的基本认证,抢占和“受质询的基本认证”。
1273 |
1274 | ### 抢占式 ###
1275 |
1276 | 服务器在某些情况下给出未授权响应之前发送基本认证凭证,从而减少进行附加连接的开销。 大多数情况下可以这么使用:
1277 |
1278 | ```java
1279 | given().auth().preemptive().basic("username", "password").when().get("/secured/hello").then().statusCode(200);
1280 | ```
1281 |
1282 | ### 受质询的基本认证 ###
1283 |
1284 | 使用“受质询的基本认证”时,REST Assured将不提供凭据,除非服务器已明确要求。 这意味着REST Assured将向服务器发出一个附加请求,以便进行质询,然后再次处理相同的请求,但此时会在header中设置基本凭据。
1285 |
1286 | ```java
1287 | given().auth().basic("username", "password").when().get("/secured/hello").then().statusCode(200);
1288 | ```
1289 |
1290 | ## 摘要认证 ##
1291 |
1292 | 目前只支持受质询的摘要认证:
1293 |
1294 |
1295 | ```java
1296 | given().auth().digest("username", "password").when().get("/secured"). ..
1297 | ```
1298 |
1299 | ## 表单认证 ##
1300 |
1301 | [表单认证](https://en.wikipedia.org/wiki/Form-based_authentication)在互联网上非常流行。 它通常与用户在网页上填写其凭据(用户名和密码),然后在按某种类型的登录按钮时发起请求。 提供表单身份验证基础的一个非常简单的HTML页面可能如下所示
1302 |
1303 | ```html
1304 |
1305 |
1306 | Login
1307 |
1308 |
1309 |
1310 |
1317 |
1318 |
1319 | ```
1320 |
1321 | 也就是说 服务器期望用户填写“j_username”和“j_password”输入字段,然后按“提交”登录。 使用REST Assured,您可以测试受表单身份验证保护的服务,如下所示:
1322 |
1323 | ```java
1324 | given().
1325 | auth().form("John", "Doe").
1326 | when().
1327 | get("/formAuth");
1328 | then().
1329 | statusCode(200);
1330 | ```
1331 | 在REST中使用此类表单身份验证时,会导致为检索包含登录详细信息的网页而向服务器发出附加请求。 REST Assured将尝试解析此页面并查找两个输入字段(用户名和密码)以及表单操作的URI。 这可能失败,取决于网页的复杂性。 更好的选择是在设置表单身份验证时提供这些详细信息。 在这种情况下,可以:
1332 |
1333 | ```java
1334 | given().
1335 | auth().form("John", "Doe", new FormAuthConfig("/j_spring_security_check", "j_username", "j_password")).
1336 | when().
1337 | get("/formAuth");
1338 | then().
1339 | statusCode(200);
1340 | ```
1341 | 这样REST Assured不需要提出额外的请求并解析网页。 还有一个预定义的FormAuthConfig称为`springSecurity`,如果你使用默认的Spring Security属性,可以使用它:
1342 | ```java
1343 | given().
1344 | auth().form("John", "Doe", FormAuthConfig.springSecurity()).
1345 | when().
1346 | get("/formAuth");
1347 | then().
1348 | statusCode(200);
1349 | ```
1350 |
1351 | ### CSRF ###
1352 | 如今,服务器要求请求中提供一个[CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) token是常有的事了,这可以抵御多种类型的攻击。rest-assured支持解析并自动给服务器供应一个CSRF token。为此,rest-assured必须先发起一个追加请求来解析该网站(的部分内容)。
1353 |
1354 | 你可以通过下面的代码启用对CSRF的支持:
1355 |
1356 | ```java
1357 | given().
1358 | auth().form("John", "Doe", formAuthConfig().withAutoDetectionOfCsrf()).
1359 | when().
1360 | get("/formAuth");
1361 | then().
1362 | statusCode(200);
1363 | ```
1364 |
1365 | 现在rest-assured将会自动尝试侦测这个网站是否包含CSRF token机制。为了使rest-assured的暴力破解更加顺利,可能会提供一个CSRF域的名称(这里我们假设我们正在使用Spring的安全默认值,因此我们可以使用预定义的`springSecurity`表单认证配置):
1366 |
1367 | ```java
1368 | given().
1369 | auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf")).
1370 | when().
1371 | get("/formAuth");
1372 | then().
1373 | statusCode(200);
1374 | ```
1375 |
1376 | 我们至此已经告诉rest-assured去查找名为"_csrf"的CSRF域了(然而这虽然会比自动侦测更快,也更容易出错)。
1377 |
1378 | 默认情况下CSRF值将会作为一个请求参数,但是如果必要你也可以配置其放在请求的header中:
1379 |
1380 | ```java
1381 | given().
1382 | auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf").sendCsrfTokenAsHeader()).
1383 | when().
1384 | get("/formAuth");
1385 | then().
1386 | statusCode(200);
1387 | ```
1388 |
1389 | ## OAuth ##
1390 |
1391 | 为了使用OAuth1和OAuth2(关于查询/请求参数签名方面的机制),您需要添加[Scribe](https://github.com/fernandezpablo85/scribe-java)到classpath中(如果你正在使用2.1.0或者更早之前版本的rest-assured,请参考[旧版指南](https://github.com/rest-assured/rest-assured/wiki/Usage_Legacy#OAuth))。如果是maven请添加以下的依赖:
1392 | ```xml
1393 |
1394 | org.scribe
1395 | scribe
1396 | 1.3.7
1397 | test
1398 |
1399 | ```
1400 |
1401 | 如果您没有使用maven,可以[下载](https://github.com/fernandezpablo85/scribe-java/releases)一个Scribe发行包并把它发在classpath下。
1402 |
1403 | ### OAuth 1 ###
1404 | OAuth1要求[Scribe](#oauth)在classpath中。为使用auth1的认证您可以:
1405 | ```java
1406 | given().auth().oauth(..). ..
1407 | ```
1408 |
1409 | ### OAuth 2 ###
1410 | 自从2.5.0版本您可以依赖于[Scribe](#oauth)使用OAuth2的认证:
1411 | ```java
1412 | given().auth().oauth2(accessToken). ..
1413 | ```
1414 | 这将会把OAuth2的`accessToken`放入header中。想要更加显式的操作可以:
1415 | ```java
1416 | given().auth().preemptive().oauth2(accessToken). ..
1417 | ```
1418 | 这里之所以存在`given().auth().oauth2(..)`这种语法是为了向后兼容(做的是相同的事情)。如果你需要在请求参数中提供一个OAuth2 token,您需要把[Scribe](#oauth)放在classpath下,接下来:
1419 | ```java
1420 | given().auth().oauth2(accessToken, OAuthSignature.QUERY_STRING). ..
1421 | ```
1422 |
1423 | ## 自定义身份验证 ##
1424 | rest-assured允许您创建一个自定义的身份验证。你可以通过实现`io.restassured.spi.AuthFilter`接口,并作为一个过滤器。假设您的安全机制,是由两个header值相加然后组成一个新的叫做"AUTH"的header(当然这并不安全)。然后您可以这样做(Java 8的语法):
1425 | ```java
1426 | given().
1427 | filter((requestSpec, responseSpec, ctx) -> {
1428 | String header1 = requestSpec.getHeaders().getValue("header1");
1429 | String header2 = requestSpec.getHeaders().getValue("header2");
1430 | requestSpec.header("AUTH", header1 + header2);
1431 | return ctx.next(requestSpec, responseSpec);
1432 | }).
1433 | when().
1434 | get("/customAuth").
1435 | then().
1436 | statusCode(200);
1437 | ```
1438 |
1439 | 使用`AuthFilter`而不是`Filter`的原因是,当我们执行`given().auth().none(). ..`类似这样的操作时`AuthFilters`会被自动移除。
1440 |
1441 | # Multi-part 表单数据 #
1442 | 通常我们在向服务器传输大容量的数据时(译者注:比如文件)会使用multipart表单数据技术。rest-assured提供了一种`multiPart`方法来辨别这究竟是文件、二进制序列、输入流还是上传的文本。表单中上传一个文件可以这样:
1443 |
1444 | ```java
1445 | given().
1446 | multiPart(new File("/path/to/file")).
1447 | when().
1448 | post("/upload");
1449 | ```
1450 |
1451 | 它将会假设有一个control叫做"file"。在HTML中这意味着input标签的属性值为file。为了解释得更清楚请看下面的HTML表单:
1452 |
1453 | ```html
1454 |
1458 | ```
1459 |
1460 | 在这个例子中control的名字就是一个属性名为file的input标签。如果您使用的control名不是这个,需要指定:
1461 |
1462 | ```java
1463 | given().
1464 | multiPart("controlName", new File("/path/to/file")).
1465 | when().
1466 | post("/upload");
1467 | ```
1468 |
1469 | 在同一个请求中提供多个"multi-parts"事务也是可能的:
1470 |
1471 | ```java
1472 | byte[] someData = ..
1473 | given().
1474 | multiPart("controlName1", new File("/path/to/file")).
1475 | multiPart("controlName2", "my_file_name.txt", someData).
1476 | multiPart("controlName3", someJavaObject, "application/json").
1477 | when().
1478 | post("/upload");
1479 | ```
1480 |
1481 | 更多高级使用方法可以使用[MultiPartSpecBuilder](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/builder/MultiPartSpecBuilder.html)。举个例子:
1482 |
1483 | ```java
1484 | Greeting greeting = new Greeting();
1485 | greeting.setFirstName("John");
1486 | greeting.setLastName("Doe");
1487 |
1488 | given().
1489 | multiPart(new MultiPartSpecBuilder(greeting, ObjectMapperType.JACKSON_2)
1490 | .fileName("greeting.json")
1491 | .controlName("text")
1492 | .mimeType("application/vnd.custom+json").build()).
1493 | when().
1494 | post("/multipart/json").
1495 | then().
1496 | statusCode(200);
1497 | ```
1498 | 你可以通过使用[MultiPartConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/MultiPartConfig.html)指定默认的control名和文件名。举个例子:
1499 |
1500 | ```java
1501 | given().config(config().multiPartConfig(multiPartConfig().defaultControlName("something-else"))). ..
1502 | ```
1503 |
1504 | 这就会默认把control名配置为"something-else"而不是"file"。
1505 |
1506 | 其它用法请查阅 [这篇博客](http://blog.jayway.com/2011/09/15/multipart-form-data-file-uploading-made-simple-with-rest-assured/)。
1507 |
1508 | # 对象映射 #
1509 | rest-assured支持从JSON和XML中映射Java对象。映射JSON需要classpath中有Jackson或者Gson才能使用,XML则需要JAXB。
1510 |
1511 | ## 序列化 ##
1512 | 假设我们有下面的Java对象:
1513 |
1514 | ```java
1515 | public class Message {
1516 | private String message;
1517 |
1518 | public String getMessage() {
1519 | return message;
1520 | }
1521 |
1522 | public void setMessage(String message) {
1523 | this.message = message;
1524 | }
1525 | }
1526 | ```
1527 |
1528 | 您需要将这个对象序列化为JSON并发送到请求中。可以有多种方式:
1529 |
1530 | ### 基于Content-Type的序列化 ###
1531 |
1532 | ```java
1533 | Message message = new Message();
1534 | message.setMessage("My messagee");
1535 | given().
1536 | contentType("application/json").
1537 | body(message).
1538 | when().
1539 | post("/message");
1540 | ```
1541 |
1542 | 在这个例子里,由于请求中的content-type被设置为"application/json",rest-assured也就会把对象序列化为JSON。rest-assured首先会在您的classpath中寻找Jackson,如果没有则使用Gson。如果您把请求中的content-type修改为"application/xml",rest-assured将会使用JAXB把对象序列化为XML。如果没有指定content-type,rest-assured会按照以下的优先级进行序列化:
1543 |
1544 | 1. 使用Jackson 2将对象序列化为JSON(Faster Jackson (databind))
1545 | 1. 使用Jackson将对象序列化为JSON(databind)
1546 | 1. 使用Gson将对象序列化为JSON
1547 | 1. 使用JAXB将对象序列化为XML
1548 |
1549 | rest-assured也关心content-type的字符集(charset)等等。
1550 |
1551 | ```java
1552 | Message message = new Message();
1553 | message.setMessage("My messagee");
1554 | given().
1555 | contentType("application/json; charset=UTF-16").
1556 | body(message).
1557 | when().
1558 | post("/message");
1559 | ```
1560 |
1561 | 您也可以把`Message`这个实例序列化为一个表单参数:
1562 | ```java
1563 | Message message = new Message();
1564 | message.setMessage("My messagee");
1565 | given().
1566 | contentType("application/json; charset=UTF-16").
1567 | formParam("param1", message).
1568 | when().
1569 | post("/message");
1570 | ```
1571 |
1572 |
1573 | 这个message对象将会被实例化为utf-16编码的JSON(如果有Jackson或者Gson)。
1574 |
1575 |
1576 | ### 由HashMap创建JSON ###
1577 |
1578 | 您也可以提供一个Map,由此rest-assured可以创建一个JSON。
1579 | ```java
1580 | Map jsonAsMap = new HashMap<>();
1581 | jsonAsMap.put("firstName", "John");
1582 | jsonAsMap.put("lastName", "Doe");
1583 |
1584 | given().
1585 | contentType(JSON).
1586 | body(jsonAsMap).
1587 | when().
1588 | post("/somewhere").
1589 | then().
1590 | statusCode(200);
1591 | ```
1592 |
1593 | 这将会产生一个JSON数据(JSON payload):
1594 |
1595 | ```javascript
1596 | { "firstName" : "John", "lastName" : "Doe" }
1597 | ```
1598 |
1599 | ### 使用显式序列化器 ###
1600 | 如果您的classpath中同时有多个对象、或者不考虑content-type的设置,可以显示地指定一个序列化器。
1601 |
1602 | ```java
1603 | Message message = new Message();
1604 | message.setMessage("My messagee");
1605 | given().
1606 | body(message, ObjectMapperType.JAXB).
1607 | when().
1608 | post("/message");
1609 | ```
1610 |
1611 | 在这个例子中message对象将会被JAXB序列化为一个XML。
1612 |
1613 | ## 反序列化 ##
1614 | 让我们再次假设我们有以下的Java对象:
1615 |
1616 | ```java
1617 | public class Message {
1618 | private String message;
1619 |
1620 | public String getMessage() {
1621 | return message;
1622 | }
1623 |
1624 | public void setMessage(String message) {
1625 | this.message = message;
1626 | }
1627 | }
1628 | ```
1629 |
1630 | 我们需要把响应体反序列化为一个Message对象。
1631 |
1632 | ### 基于Content-Type的反序列化 ###
1633 | 假设服务端返回一个这样的JSON:
1634 | ```javascript
1635 | {"message":"My message"}
1636 | ```
1637 |
1638 | 将它反序列化为一个Message对象:
1639 | ```java
1640 | Message message = get("/message").as(Message.class);
1641 | ```
1642 |
1643 | 为此响应体的content-type必须是"application/json"(或者其它包含“json”的类型)。如果服务端返回:
1644 |
1645 | ```xml
1646 |
1647 |
1648 | My message
1649 |
1650 | ```
1651 |
1652 | 且content-type是"application/xml",代码可以完全不用修改:
1653 | ```java
1654 | Message message = get("/message").as(Message.class);
1655 | ```
1656 |
1657 | #### 自定义类型content-type反序列化 ####
1658 | 如果服务端返回一个自定义的content-type,假设是"application/something",你仍然想使用rest-assured的对象映射的话,这有两种方法。你可以使用[显式指定](http://code.google.com/p/rest-assured/wiki/Usage#Using_an_Explicit_Deserializer)的方法或者为自定义的content-type注册一个解析器:
1659 |
1660 | ```java
1661 | Message message = expect().parser("application/something", Parser.XML).when().get("/message").as(Message.class);
1662 | ```
1663 |
1664 | 或
1665 |
1666 | ```java
1667 | Message message = expect().defaultParser(Parser.XML).when().get("/message").as(Message.class);
1668 | ```
1669 |
1670 | 你也可以注册一个默认解析器,或者静态式注册一个自定义的解析器,也可以使用[模式(specifications)](#模式复用)。
1671 |
1672 | ### 使用显式反序列化器 ###
1673 | 如果您的classpath下同时有多个对象或者不在意响应体的content-type,你可以使用显示的反序列化器。
1674 |
1675 | ```java
1676 | Message message = get("/message").as(Message.class, ObjectMapperType.GSON);
1677 | ```
1678 |
1679 | ## 配置 ##
1680 | 您可以使用[ObjectMapperConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/ObjectMapperConfig.html)配置预定义的对象映射,并传递给[细节配置](#detailed-configuration)。举个例子,你可以将GSON的命名策略改为LowerCaseWithUnderscores(译者注:一种策略,将大写字母改为小写字母并添加下划线):
1681 |
1682 | ```java
1683 | RestAssured.config = RestAssuredConfig.config().objectMapperConfig(objectMapperConfig().gsonObjectMapperFactory(
1684 | new GsonObjectMapperFactory() {
1685 | public Gson create(Class cls, String charset) {
1686 | return new GsonBuilder().setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
1687 | }
1688 | }
1689 | ));
1690 | ```
1691 |
1692 | 这里为GSON、JAXB、Jackson和Faster Jackson都预定义了用来映射实例的工厂。
1693 |
1694 | ## 自定义 ##
1695 | 默认情况下rest-assured将会扫描classpath中各种各样的对象映射。如果您想要集成一种对象映射,默认是不支持的,如果您已经做好了封装,可以实现[io.restassured.mapper.ObjectMapper](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/mapper/ObjectMapper.html) 接口。告诉rest-assured使用您的对象映射或者将其作为body方法里的第二个参数:
1696 |
1697 | ```java
1698 | given().body(myJavaObject, myObjectMapper).when().post("..")
1699 | ```
1700 |
1701 | 或者您可以静态式定义:
1702 | ```java
1703 | RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig(myObjectMapper));
1704 | ```
1705 |
1706 | 更多例子参阅[这里](https://github.com/rest-assured/rest-assured/blob/master/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/CustomObjectMappingITest.java)。
1707 |
1708 | # 自定义解析器 #
1709 | rest-assured提供了预定义的解析器,例如HTML、XML和JSON的。但是您可以通过注册预置的解析器来解析现在不支持的内容类型:
1710 | ```java
1711 | RestAssured.registerParser(, );
1712 | ```
1713 | 例如注册一个可以解析'application/vnd.uoml+xml'类型的XML解析器:
1714 | ```java
1715 | RestAssured.registerParser("application/vnd.uoml+xml", Parser.XML);
1716 | ```
1717 | 您也可以注销一个解析器:
1718 | ```java
1719 | RestAssured.unregisterParser("application/vnd.uoml+xml");
1720 | ```
1721 |
1722 | 解析器可以指定于每个请求中:
1723 | ```java
1724 | get(..).then().using().parser("application/vnd.uoml+xml", Parser.XML). ..;
1725 | ```
1726 |
1727 | 然后使用[模式](#模式复用)。
1728 |
1729 | # 默认解析器 #
1730 | 有时如果响应中不包含任何content-type,指定一个默认的解析器会很有用。
1731 |
1732 | ```java
1733 | RestAssured.defaultParser = Parser.JSON;
1734 | ```
1735 |
1736 | 你也可以为一次请求指定默认的解析器:
1737 | ```java
1738 | get("/x").then().using().defaultParser(Parser.JSON). ..
1739 | ```
1740 |
1741 | 或者使用[响应体模式](#模式复用)。
1742 |
1743 | # 默认值 #
1744 | rest-assured发起请求时默认使用localhost的8080端口.如果你需要换个端口:
1745 | ```java
1746 | given().port(80). ..
1747 | ```
1748 | 或者简单些:
1749 | ```java
1750 | ..when().get("http://myhost.org:80/doSomething");
1751 | ```
1752 | 您也可以改变默认的基本URI、基本路径、端口和认证scheme:
1753 | ```java
1754 | RestAssured.baseURI = "http://myhost.org";
1755 | RestAssured.port = 80;
1756 | RestAssured.basePath = "/resource";
1757 | RestAssured.authentication = basic("username", "password");
1758 | RestAssured.rootPath = "x.y.z";
1759 | ```
1760 | 这意味着类似`get("/hello")`这样的请求实际上会被解析为http://myhost.org:80/resource/hello,附带身份认证中的用户名和密码属性。关于设置根路径参考[这里](http://code.google.com/p/rest-assured/wiki/Usage#Root_path)。其它的默认值也可以被指定:
1761 |
1762 | ```java
1763 | RestAssured.filters(..); // List of default filters
1764 | RestAssured.requestSpecification = .. // Default request specification
1765 | RestAssured.responseSpecification = .. // Default response specification
1766 | RestAssured.urlEncodingEnabled = .. // Specify if Rest Assured should URL encoding the parameters
1767 | RestAssured.defaultParser = .. // Specify a default parser for response bodies if no registered parser can handle data of the response content-type
1768 | RestAssured.registerParser(..) // Specify a parser for the given content-type
1769 | RestAssured.unregisterParser(..) // Unregister a parser for the given content-type
1770 | ```
1771 |
1772 | 您可以设置重置为标准的baseURI (localhost),basePath(空),标准端口(8080),标准根路径(“”),默认身份认证scheme(none)和URL编码启用(true):
1773 | ```java
1774 | RestAssured.reset();
1775 | ```
1776 |
1777 | # 模式复用 #
1778 | 与其复制一份响应的断言或者请求参数(的代码)到另一个测试用例中,我们可以使用 [RequestSpecBuilder](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/builder/RequestSpecBuilder.html)或者[ResponseSpecBuilder](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/builder/ResponseSpecBuilder.html)定义一个规范提案。
1779 |
1780 | 举个例子,在多个测试用例中,我们都涉及到这样的内容:判断响应码是否为200,JSON数组x.y的长度是否是2,您可以定义一个ResponseSpecBuilder:
1781 |
1782 | ```java
1783 | ResponseSpecBuilder builder = new ResponseSpecBuilder();
1784 | builder.expectStatusCode(200);
1785 | builder.expectBody("x.y.size()", is(2));
1786 | ResponseSpecification responseSpec = builder.build();
1787 |
1788 | // Now you can re-use the "responseSpec" in many different tests:
1789 | when().
1790 | get("/something").
1791 | then().
1792 | spec(responseSpec).
1793 | body("x.y.z", equalTo("something"));
1794 | ```
1795 |
1796 | 在这个例子中需要重用的数据定义并合并在"responseSpec",并且仅当所有预期都通过时用例才能通过。
1797 |
1798 | 您也可以将相同的请求参数重用:
1799 | ```java
1800 | RequestSpecBuilder builder = new RequestSpecBuilder();
1801 | builder.addParam("parameter1", "parameterValue");
1802 | builder.addHeader("header1", "headerValue");
1803 | RequestSpecification requestSpec = builder.build();
1804 |
1805 | given().
1806 | spec(requestSpec).
1807 | param("parameter2", "paramValue").
1808 | when().
1809 | get("/something").
1810 | then().
1811 | body("x.y.z", equalTo("something"));
1812 | ```
1813 |
1814 | 这里请求数据合并在"requestSpec"中,由此上面例子中实际请求参数包括两个("parameter1" 和 "parameter2")和一个header("header1")。
1815 |
1816 | # 过滤器 #
1817 | 过滤器会在请求实际发起之前侦测和改变该请求的内容,也可以在响应体实际返回之前拦截并[改变](#response-builder)。您可以将其理解为AOP中的around advice(译者注:可以自行搜索切片编程)。过滤器也可以用在认证scheme、session管理、日志中。创建一个过滤器需要实现[io.restassured.filter.Filter](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/filter/Filter.html)接口。使用过滤器:
1818 |
1819 | ```java
1820 | given().filter(new MyFilter()). ..
1821 | ```
1822 |
1823 | rest-assured提供了几个过滤器:
1824 | 1. `io.restassured.filter.log.RequestLoggingFilter`: 可以打印出请求模式的细节。
1825 | 1. `io.restassured.filter.log.ResponseLoggingFilter`: 可以打印响应信息的细节如果响应体的状态码匹配given方法的参数。
1826 | 1. `io.restassured.filter.log.ErrorLoggingFilter`: 如果发生了异常(状态码在400和500之间),过滤器将会打印响应的内容。
1827 |
1828 |
1829 | ## Response Builder ##
1830 |
1831 | 如果您想要通过一个过滤器改变[Response](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/Response.html) ,可以使用[ResponseBuilder](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/builder/ResponseBuilder.html)创建一个基于原始response的新实例。比如把原始响应体改为something:
1832 | ```java
1833 | Response newResponse = new ResponseBuilder().clone(originalResponse).setBody("Something").build();
1834 | ```
1835 |
1836 | # 日志 #
1837 | 在大量的用例中,打印出响应或者请求的细节将有助于创建正确的预期、发送准确的请求。为此您可以使用rest-assured预定义的[过滤器](#过滤器),或者使用其中的快捷方法。
1838 |
1839 | ## 请求日志 ##
1840 | 自1.5版本起rest-assured支持对[特定的请求](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/specification/RequestSpecification.html)打日志,之前的做法是使用[RequestLoggingFilter(译者注:应该是通过过滤器进行控制)](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/filter/log/RequestLoggingFilter.html)。注意打印日志后HTTP Builder和HTTP Client会添加额外的header内容。这个过滤器可以只记录特定请求的特定细节。换句话说,你可以不关注[RequestLoggingFilter](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/filter/log/RequestLoggingFilter.html)记录的有关实际往服务端发送的内容。因为随后的其它过滤器会在日志记录后改变这个请求。如果你想要记录实际发送的内容,参阅[HTTP Client logging docs](http://hc.apache.org/httpcomponents-client-ga/logging.html) or use an external tool such [Wireshark](http://www.wireshark.org/),或者使用第三方工具例如[Wireshark](http://www.wireshark.org/)。示例如下:
1841 | ```java
1842 | given().log().all(). .. // Log all request specification details including parameters, headers and body
1843 | given().log().params(). .. // Log only the parameters of the request
1844 | given().log().body(). .. // Log only the request body
1845 | given().log().headers(). .. // Log only the request headers
1846 | given().log().cookies(). .. // Log only the request cookies
1847 | given().log().method(). .. // Log only the request method
1848 | given().log().path(). .. // Log only the request path
1849 | ```
1850 |
1851 | ## 响应日志 ##
1852 | 如果您想打印除了状态码以外的响应信息,可以:
1853 |
1854 | ```java
1855 | get("/x").then().log().body() ..
1856 | ```
1857 |
1858 | 这样做,无论是否有异常错误发生,都会打印出响应信息。如果您希望只有当错误发生时才打印响应信息,可以:
1859 |
1860 | ```java
1861 | get("/x").then().log().ifError(). ..
1862 | ```
1863 |
1864 | 您也可以记录响应里包括状态码、header、cookie的所有细节:
1865 |
1866 | ```java
1867 | get("/x").then().log().all(). ..
1868 | ```
1869 |
1870 | 也可以只记录状态码、header或者cookie:
1871 | ```java
1872 | get("/x").then().log().statusLine(). .. // Only log the status line
1873 | get("/x").then().log().headers(). .. // Only log the response headers
1874 | get("/x").then().log().cookies(). .. // Only log the response cookies
1875 | ```
1876 |
1877 | 您也可以配置为仅当状态码匹配某个值时才打印响应体:
1878 | ```java
1879 | get("/x").then().log().ifStatusCodeIsEqualTo(302). .. // Only log if the status code is equal to 302
1880 | get("/x").then().log().ifStatusCodeMatches(matcher). .. // Only log if the status code matches the supplied Hamcrest matcher
1881 | ```
1882 |
1883 | ## 认证失败日志 ##
1884 |
1885 | 自rest-assured2.3.1版本起,您可以仅当认证失败时记录请求或者响应的日志。为请求打日志:
1886 | ```java
1887 | given().log().ifValidationFails(). ..
1888 | ```
1889 |
1890 | 为响应打日志:
1891 | ```java
1892 | .. .then().log().ifValidationFails(). ..
1893 | ```
1894 |
1895 | 同时启用对请求和响应的认证失败记录,可以使用[LogConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/LogConfig.html):
1896 |
1897 | ```java
1898 | given().config(RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails(HEADERS))). ..
1899 | ```
1900 |
1901 | 认证失败,仅记录header。
1902 |
1903 | 还有种针对所有请求的简单写法:
1904 |
1905 | ```java
1906 | RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
1907 | ```
1908 |
1909 | # 根路径 #
1910 | 为避免在body方法里使用重复的路径,您可以指定一个根路径:
1911 | ```java
1912 | when().
1913 | get("/something").
1914 | then().
1915 | body("x.y.firstName", is(..)).
1916 | body("x.y.lastName", is(..)).
1917 | body("x.y.age", is(..)).
1918 | body("x.y.gender", is(..));
1919 | ```
1920 |
1921 | 使用根路径:
1922 |
1923 | ```java
1924 | when().
1925 | get("/something").
1926 | then().
1927 | root("x.y"). // You can also use the "root" method
1928 | body("firstName", is(..)).
1929 | body("lastName", is(..)).
1930 | body("age", is(..)).
1931 | body("gender", is(..));
1932 | ```
1933 | 也可以设置一个默认的根路径:
1934 | ```java
1935 | RestAssured.rootPath = "x.y";
1936 | ```
1937 |
1938 | 在许多高级用例中,在根路径上附加一些参数也很有用。您可以使用`appendRoot`方法:
1939 | ```java
1940 | when().
1941 | get("/jsonStore").
1942 | then().
1943 | root("store.%s", withArgs("book")).
1944 | body("category.size()", equalTo(4)).
1945 | appendRoot("%s.%s", withArgs("author", "size()")).
1946 | body(withNoArgs(), equalTo(4));
1947 | ```
1948 |
1949 | 也可以对根路径进行拆分:
1950 | ```java
1951 | when().
1952 | get("/jsonStore").
1953 | then().
1954 | root("store.category").
1955 | body("size()", equalTo(4)).
1956 | detachRoot("category").
1957 | body("size()", equalTo(1));
1958 | ```
1959 |
1960 | # 路径参数 #
1961 | 在预定义的路径包含变量时,路径参数会很有用。举个例子:
1962 | ```java
1963 | String someSubPath = "else";
1964 | int index = 1;
1965 | get("/x").then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..
1966 | ```
1967 |
1968 | 将会对"`something.else[0](译者注:这里只是举个例子)`"是否等于"some value"进行判断。
1969 |
1970 | 另一种用法是,如果您有复杂的[根路径](http://code.google.com/p/rest-assured/wiki/Usage#Root_path):
1971 | ```java
1972 | when().
1973 | get("/x").
1974 | then().
1975 | root("filters.filterConfig[%d].filterConfigGroups.find { it.name == 'GroupName' }.includes").
1976 | body(withArgs(0), hasItem("first")).
1977 | body(withArgs(1), hasItem("second")).
1978 | ..
1979 | ```
1980 |
1981 | 路径参数遵循Java的标准[格式语法](http://download.oracle.com/javase/1,5.0/docs/api/java/util/Formatter.html#syntax)。
1982 |
1983 | 注意`withArgs`方法可以从[io.restassured.RestAssured](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/RestAssured.html)类中静态导入。
1984 |
1985 | 有时当所有在根路径中指定的参数都已经验证过了,只想要验证一个不含多余参数的body时,可以使用`withNoArgs`:
1986 | ```java
1987 | when().
1988 | get("/jsonStore").
1989 | then().
1990 | root("store.%s", withArgs("book")).
1991 | body("category.size()", equalTo(4)).
1992 | appendRoot("%s.%s", withArgs("author", "size()")).
1993 | body(withNoArgs(), equalTo(4));
1994 | ```
1995 |
1996 | # Session支持 #
1997 | rest-assured提供了一套简单的管理session的方式。您可以在DSL(译者注:领域特定语言)中预定义一个session的id值:
1998 | ```java
1999 | given().sessionId("1234"). ..
2000 | ```
2001 |
2002 | 上面的代码实际上是这个的简写:
2003 | ```java
2004 | given().cookie("JSESSIONID", "1234"). ..
2005 | ```
2006 |
2007 | 您也可以为所有的请求指定一个默认的`sessionId`:
2008 | ```java
2009 | RestAssured.sessionId = "1234";
2010 | ```
2011 |
2012 | 默认情况下session id的名字是`JSESSIONID`,但是您可以通过使用[SessionConfig](#session-config)来修改:
2013 | ```java
2014 | RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));
2015 | ```
2016 |
2017 | 您也可以指定一个sessionid并在其它用例中复用,使用`RequestSpecBuilder`:
2018 | ```java
2019 | RequestSpecBuilder spec = new RequestSpecBuilder().setSessionId("value1").build();
2020 |
2021 | // Make the first request with session id equal to value1
2022 | given().spec(spec). ..
2023 | // Make the second request with session id equal to value1
2024 | given().spec(spec). ..
2025 | ```
2026 |
2027 | 从响应对象中获取一个session id:
2028 | ```java
2029 | String sessionId = get("/something").sessionId();
2030 | ```
2031 |
2032 | ## Session过滤器 ##
2033 | 2.0.0版本起您可以使用[session filter](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/filter/session/SessionFilter.html)截获并提供一个session,举个例子:
2034 | ```java
2035 | SessionFilter sessionFilter = new SessionFilter();
2036 |
2037 | given().
2038 | auth().form("John", "Doe").
2039 | filter(sessionFilter).
2040 | when().
2041 | get("/formAuth").
2042 | then().
2043 | statusCode(200);
2044 |
2045 |
2046 | given().
2047 | filter(sessionFilter). // Reuse the same session filter instance to automatically apply the session id from the previous response
2048 | when().
2049 | get("/x").
2050 | then().
2051 | statusCode(200);
2052 | ```
2053 |
2054 | 要想获取由`SessionFilter`截获的session id:
2055 | ```java
2056 | String sessionId = sessionFilter.getSessionId();
2057 | ```
2058 |
2059 | # SSL #
2060 |
2061 | 在多数场景下,SSL能顺利运转,这多亏于HTTP Builder和HTTP Client。如果服务端使用了无效的证书,然而有些例子下还是会出错。最简单的方法您可以使用"relaxed HTTPs validation":
2062 | ```java
2063 | given().relaxedHTTPSValidation().when().get("https://some_server.com"). ..
2064 | ```
2065 | 也可以为所有的请求静态定义这个配置:
2066 | ```java
2067 | RestAssured.useRelaxedHTTPSValidation();
2068 | ```
2069 | 或者放在请求指定中进行复用。
2070 |
2071 | 这将会假定我们使用SSL协议。想要改变需要覆盖`relaxedHTTPSValidation`方法:
2072 |
2073 | ```java
2074 | given().relaxedHTTPSValidation("TLS").when().get("https://some_server.com"). ..
2075 | ```
2076 |
2077 | 您也可以创建一个Java keystore文件并执行一些细粒度的操作。这并不难,首先阅读[指南](https://github.com/jgritman/httpbuilder/wiki/SSL),然后在rest-assured中使用keystore:
2078 |
2079 | ```java
2080 | given().keystore("/pathToJksInClassPath", ). ..
2081 | ```
2082 |
2083 | 或者为每一个请求指定:
2084 |
2085 | ```java
2086 | RestAssured.keystore("/pathToJksInClassPath", );
2087 | ```
2088 |
2089 | 您也可以定义一个[可复用的](http://code.google.com/p/rest-assured/wiki/Usage#Specification_Re-use)keystore。
2090 |
2091 | 如果您已经使用密码载入了一个keystore,可以将其作为可信的keystore:
2092 | ```java
2093 | RestAssured.trustStore(keystore);
2094 | ```
2095 |
2096 | 在[这](https://github.com/rest-assured/rest-assured/blob/master/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java/SSLTest.java)也可以找到一些相关的示例。
2097 |
2098 | 有关更多SSL的高级配置参阅[SSL Configuration](#ssl配置)。
2099 |
2100 | ## SSL 无效主机名 ##
2101 | 如果证书指定了一个无效的主机名,并且您无需创建和导入一个keystore。自2.2.0起您可以:
2102 | ```java
2103 | RestAssured.config = RestAssured.config().sslConfig(sslConfig().allowAllHostnames());
2104 | ```
2105 |
2106 | 让所有请求支持所有的主机名。
2107 |
2108 | ```java
2109 | given().config(RestAssured.config().sslConfig(sslConfig().allowAllHostnames()). .. ;
2110 | ```
2111 | 对于单个请求。
2112 |
2113 | 注意如果您使用了"relaxed HTTPs validation",那么`allowAllHostnames`将会默认启用。
2114 |
2115 | # URL 编码 #
2116 | 由于rest-assured提供了优秀的自动编码,通常您无需考虑URL编码的事情。在有些用例中仍然需要关闭URL编码的功能。其中一个原因是在使用rest-assured之前可能你已经做过一次编码。为防止两次URL编码您需要告诉rest-assured禁用这个功能。
2117 |
2118 | ```java
2119 | String response = given().urlEncodingEnabled(false).get("https://jira.atlassian.com:443/rest/api/2.0.alpha1/search?jql=project%20=%20BAM%20AND%20issuetype%20=%20Bug").asString();
2120 | ..
2121 | ```
2122 |
2123 | 或者
2124 |
2125 | ```java
2126 | RestAssured.baseURI = "https://jira.atlassian.com";
2127 | RestAssured.port = 443;
2128 | RestAssured.urlEncodingEnabled = false;
2129 | final String query = "project%20=%20BAM%20AND%20issuetype%20=%20Bug";
2130 | String response = get("/rest/api/2.0.alpha1/search?jql={q}", query);
2131 | ..
2132 | ```
2133 |
2134 | # 代理(proxy)配置 #
2135 |
2136 | 从版本2.3.2开始REST Assured可以更好地支持代理。 例如,如果你有一个代理在localhost端口8888你可以做:
2137 | ```java
2138 | given().proxy("localhost", 8888). ..
2139 | ```
2140 | 如果服务器在本地环境中运行,可以不指定主机名:
2141 | ```java
2142 | given().proxy(8888). .. // Will assume localhost
2143 | ```
2144 | 要使用HTTPS,需要提供第三个参数(scheme)或使用`io.restassured.specification.ProxySpecification`。 例如:
2145 |
2146 | ```java
2147 | given().proxy(host("localhost").withScheme("https")). ..
2148 | ```
2149 |
2150 | 其中host从io.restassured.specification.ProxySpecification静态导入。
2151 |
2152 | 从版本2.7.0开始,您还可以为代理指定抢占式基本身份验证。 例如:
2153 |
2154 | ```
2155 | given().proxy(auth("username", "password")).when() ..
2156 | ```
2157 |
2158 | 其中`auth`是从[io.restassured.specification.ProxySpecification](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/specification/ProxySpecification.html)静态导入的,也可以将身份验证与不同的host相结合:
2159 | ```java
2160 | given().proxy(host("http://myhost.org").withAuth("username", "password")). ..
2161 | ```
2162 |
2163 | ## 静态代理配置 ##
2164 |
2165 | 可以为所有请求配置静态代理,例如:
2166 | ```java
2167 | RestAssured.proxy("localhost", 8888);
2168 | ```
2169 |
2170 | 或者:
2171 |
2172 | ```java
2173 | RestAssured.proxy = host("localhost").withPort(8888);
2174 | ```
2175 |
2176 | ## 请求规范代理配置 ##
2177 |
2178 | 您还可以创建请求规范并在其中指定代理:
2179 |
2180 | ```java
2181 | RequestSpecification specification = new RequestSpecBuilder().setProxy("localhost").build();
2182 | given().spec(specification). ..
2183 | ```
2184 |
2185 | # 详细配置 #
2186 |
2187 | 详细配置由[RestAssuredConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/RestAssuredConfig.html)实例提供,您可以使用它配置[HTTP Client](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/HttpClientConfig.html)的参数以及[重定向](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/RedirectConfig.html),[日志](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/LogConfig.html),[编码器](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/EncoderConfig.html),[解码器](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/DecoderConfig.html),[Session](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/SessionConfig.html),[ObjectMapper](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/ObjectMapperConfig.html),[Connection](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/ConnectionConfig.html),[SSL](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/SSLConfig.html)和[ParamConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/ParamConfig.html) 设置。 例子:
2188 |
2189 | 特定请求:
2190 | ```java
2191 | given().config(RestAssured.config().redirect(redirectConfig().followRedirects(false))). ..
2192 | ```
2193 |
2194 | 或使用RequestSpecBuilder:
2195 | ```java
2196 | RequestSpecification spec = new RequestSpecBuilder().setConfig(RestAssured.config().redirect(redirectConfig().followRedirects(false))).build();
2197 | ```
2198 |
2199 | 或所有请求:
2200 | ```java
2201 | RestAssured.config = config().redirect(redirectConfig().followRedirects(true).and().maxRedirects(0));
2202 | ```
2203 | `config()` and `newConfig()` can be statically imported from `io.restassured.config.RestAssuredConfig`.
2204 |
2205 | ## 编码配置 ##
2206 |
2207 | 使用[EncoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/EncoderConfig.html),可以指定默认的内容encoding charset(如果没有在content-type头中指定)和查询参数charset为所有请求。 如果未指定内容字符集,则使用ISO-8859-1,如果未指定查询参数字符集,则使用UTF-8。 用法示例:
2208 | ```java
2209 | RestAssured.config = RestAssured.config().encoderConfig(encoderConfig().defaultContentCharset("US-ASCII"));
2210 | ```
2211 |
2212 | 如果没有通过使用[EncoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/EncoderConfig.html)中的defaultCharsetForContentType方法为此内容类型显式定义字符集,那么还可以指定用于特定内容类型的编码器字符集。 例如:
2213 |
2214 | ```java
2215 | RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));
2216 | ```
2217 |
2218 | 这将假设明确指定一个字符集的“application / xml”内容类型的UTF-16编码。 默认情况下,“application / json”被指定为使用“UTF-8”作为默认内容类型,这是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。
2219 |
2220 | ### 避免自动将字符集添加到content-type标头 ###
2221 |
2222 | 默认情况下,REST Assured会自动添加字符集标头。 要完全禁用这个,你可以这样配置`EncoderConfig`:
2223 |
2224 | ```java
2225 | RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false));
2226 | ```
2227 |
2228 | ## 解码配置 ##
2229 |
2230 | 使用[DecoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/DecoderConfig.html),您可以将默认响应内容解码字符集设置为 所有响应。 如果您期望使用不同于ISO-8859-1的内容字符集(这是默认字符集),并且响应未在内容类型标头中定义字符集,那么这将非常有用。 用法示例:
2231 |
2232 | ```java
2233 | RestAssured.config = RestAssured.config().decoderConfig(decoderConfig().defaultContentCharset("UTF-8"));
2234 | ```
2235 |
2236 | 您还可以使用“DecoderConfig”来指定要应用哪些内容解码器。 当你这样做时,“Accept-Encoding”头部将被自动添加到请求中,并且响应主体将被自动解码。 默认情况下,启用GZIP和DEFLATE解码器。 例如要删除GZIP解码但保留DEFLATE解码,您可以执行以下操作:
2237 | 您还可以使用“DecoderConfig”来指定要应用哪些内容解码器。 当你这样做时,“Accept-Encoding”头部将被自动添加到请求中,并且响应主体将被自动解码。 默认情况下,启用GZIP和DEFLATE解码器。 例如要删除GZIP解码但保留DEFLATE解码,您可以执行以下操作:
2238 |
2239 | ```java
2240 | given().config(RestAssured.config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))). ..
2241 | ```
2242 |
2243 | 如果没有通过使用[DecoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/DecoderConfig.html)中的“defaultCharsetForContentType”方法为此内容类型明确定义字符集,则还可以指定要用于特定内容类型的解码器字符集。 例如:
2244 |
2245 | ```java
2246 | RestAssured.config = config(config().decoderConfig(decoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));
2247 | ```
2248 |
2249 | 这将假设明确指定一个字符集的“application / xml”内容类型的UTF-16编码。 默认情况下,“application / json”使用“UTF-8”作为默认字符集,这是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。
2250 |
2251 | ## Session配置 ##
2252 |
2253 | 使用Session配置,您可以配置REST Assured使用的默认session ID名称。 默认session id名称是`JSESSIONID`,如果应用程序中的名称不同,并且您希望使用REST Assured的[会话支持](#Session_support),您只需更改它。 用法:
2254 |
2255 | ```java
2256 | RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));
2257 | ```
2258 |
2259 | ## 重定向 DSL ##
2260 |
2261 | 重定向配置可以使用DSL指定。 例如。
2262 |
2263 | ```java
2264 | given().redirects().max(12).and().redirects().follow(true).when(). ..
2265 | ```
2266 |
2267 | ## 网络连接配置 ##
2268 |
2269 | 让您配置REST Assured的连接设置。 例如,如果要在每个响应后强制关闭Apache HTTP Client连接。 如果您在响应中使用少量数据进行大量快速连续请求,则可能需要执行此操作。然而,如果你正在下载(尤其是大量的)分块,你绝不能每个响应后关闭连接的数据。 默认情况下,连接在每个响应后不关闭。
2270 |
2271 | ```java
2272 | RestAssured.config = RestAssured.config().connectionConfig(connectionConfig().closeIdleConnectionsAfterEachResponse());
2273 | ```
2274 |
2275 | ## Json Config ##
2276 |
2277 | [JsonPathConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/path/json/config/JsonPathConfig.html)允许您在REST Assured或 [JsonPath](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/path/json/JsonPath.html)使用时配置Json设置。 它让你配置如何处理JSON数字。
2278 |
2279 | ```java
2280 | RestAssured.config = RestAssured.config().jsonConfig(jsonConfig().numberReturnType(NumberReturnType.BIG_DECIMAL))
2281 | ```
2282 |
2283 | ## HTTP Client 配置 ##
2284 |
2285 | 为REST Assured将在执行请求时使用的HTTP Client实例配置属性。 默认情况下,REST Assured会为每个“given”语句创建一个新的http Client实例。 要配置重用,请执行以下操作:
2286 |
2287 | ```java
2288 | RestAssured.config = RestAssured.config().httpClient(httpClientConfig().reuseHttpClientInstance());
2289 | ```
2290 | 您还可以使用`httpClientFactory`方法提供自定义HTTP Client实例,例如:
2291 |
2292 | ```java
2293 | RestAssured.config = RestAssured.config().httpClient(httpClientConfig().httpClientFactory(
2294 | new HttpClientConfig.HttpClientFactory() {
2295 |
2296 | @Override
2297 | public HttpClient createHttpClient() {
2298 | return new SystemDefaultHttpClient();
2299 | }
2300 | }));
2301 | ```
2302 |
2303 | **注意,目前你需要提供一个`AbstractHttpClient`的实例.**
2304 |
2305 | 也可以配置默认参数等。
2306 |
2307 | ## SSL 配置 ##
2308 |
2309 | [SSLConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/SSLConfig.html) 允许您指定更高级的SSL配置,如信任库,密钥库类型和主机名验证器。 例如:
2310 |
2311 | ```java
2312 | RestAssured.config = RestAssured.config().sslConfig(sslConfig().with().keystoreType().and().strictHostnames());
2313 | ```
2314 |
2315 | ## 参数配置 ##
2316 | [ParamConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/ParamConfig.html) 允许您配置在“冲突”时,更新不同的参数。 默认情况下,所有参数都将合并,因此如果您执行以下操作:
2317 |
2318 | ```java
2319 | given().queryParam("param1", "value1").queryParam("param1", "value2").when().get("/x"). ...
2320 | ```
2321 |
2322 | REST Assured将发送一个查询字符串`param1 = value1&param1 = value2`。
2323 | 如果这不是您想要的,你可以配置REST Assured为* replace *值代替:
2324 |
2325 |
2326 | ```java
2327 | given().
2328 | config(config().paramConfig(paramConfig().queryParamsUpdateStrategy(REPLACE))).
2329 | queryParam("param1", "value1").
2330 | queryParam("param1", "value2").
2331 | when().
2332 | get("/x"). ..
2333 | ```
2334 |
2335 | REST Assured现在将替换`param1`的值为`value2`(因为它是最后写的),而不是将它们合并在一起。 您也可以为所有参数类型的每种类型配置统一的更新策略
2336 |
2337 | ```java
2338 | given().config(config().paramConfig(paramConfig().replaceAllParameters())). ..
2339 | ```
2340 |
2341 | 也支持在[Spring Mock Mvc模块](# Spring Mock Mvc 模块)(配置[MockMvcParamConfig](http://static.javadoc.io/io.restassured/spring-mock -mvc / 3.0.1 / io / restassured / module / mockmvc / config / MockMvcParamConfig.html)。
2342 |
2343 |
2344 | # Spring Mock Mvc 模块 #
2345 |
2346 | REST Assured 2.2.0引入了对[Spring Mock Mvc](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html)的支持,使用 `spring-mock-mvc`模块。 这意味着您可以单元测试Spring Mvc控制器。 例如给出以下Spring控制器:
2347 |
2348 | ```java
2349 | @Controller
2350 | public class GreetingController {
2351 |
2352 | private static final String template = "Hello, %s!";
2353 | private final AtomicLong counter = new AtomicLong();
2354 |
2355 | @RequestMapping(value = "/greeting", method = GET)
2356 | public @ResponseBody Greeting greeting(
2357 | @RequestParam(value="name", required=false, defaultValue="World") String name) {
2358 | return new Greeting(counter.incrementAndGet(), String.format(template, name));
2359 | }
2360 | }
2361 | ```
2362 |
2363 | 你可以使用[RestAssuredMockMvc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)来测试它,像这样:
2364 |
2365 | ```java
2366 | given().
2367 | standaloneSetup(new GreetingController()).
2368 | param("name", "Johan").
2369 | when().
2370 | get("/greeting").
2371 | then().
2372 | statusCode(200).
2373 | body("id", equalTo(1)).
2374 | body("content", equalTo("Hello, Johan!"));
2375 | ```
2376 |
2377 | 即它与标准的REST Assured语法非常相似。 这使得运行测试真的很快,并且比标准的REST Assured更容易引导环境和使用mock。 你常用的标准REST Assured中的大多数东西都可以使用RestAssured Mock Mvc。 例如(某些)配置,静态规范,日志等等。要使用它,你需要依赖于Spring Mock Mvc模块:
2378 |
2379 | ```xml
2380 |
2381 | io.rest-assured
2382 | spring-mock-mvc
2383 | 3.0.1
2384 | test
2385 |
2386 | ```
2387 |
2388 | 或者[下载](http://dl.bintray.com/johanhaleby/generic/spring-mock-mvc-3.0.1-dist.zip)。
2389 |
2390 | ## Bootstrapping RestAssuredMockMvc ##
2391 |
2392 | 静态导入方法:
2393 | ```java
2394 | io.restassured.module.mockmvc.RestAssuredMockMvc.*
2395 | io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers.*
2396 | ```
2397 |
2398 |
2399 |
2400 | 有关其他静态导入,请参阅文档的[静态导入](#static-imports)部分。
2401 |
2402 | 为了使用RestAssuredMockMvc启动测试,您需要使用一组控制器,MockMvc实例或Spring的WebApplicationContext来初始化它。您可以对单个请求执行此操作,如上例所示:
2403 |
2404 |
2405 | ```java
2406 | given().standaloneSetup(new GreetingController()). ..
2407 | ```
2408 |
2409 | 也可以使用静态方法:
2410 |
2411 | ```java
2412 | RestAssuredMockMvc.standaloneSetup(new GreetingController());
2413 | ```
2414 |
2415 | 如果静态定义,则不必在DSL中指定任何控制器(或MockMvc或WebApplicationContext实例)。这意味着前面的例子可以写成:
2416 |
2417 | ```java
2418 | given().
2419 | param("name", "Johan").
2420 | when().
2421 | get("/greeting").
2422 | then().
2423 | statusCode(200).
2424 | body("id", equalTo(1)).
2425 | body("content", equalTo("Hello, Johan!"));
2426 | ```
2427 |
2428 | ## 异步请求 ##
2429 |
2430 | 从版本`2.5.0` RestAssuredMockMvc支持异步请求。例如,假设有以下控制器
2431 |
2432 | ```java
2433 | @Controller
2434 | public class PostAsyncController {
2435 |
2436 | @RequestMapping(value = "/stringBody", method = POST)
2437 | public @ResponseBody
2438 | Callable stringBody(final @RequestBody String body) {
2439 | return new Callable() {
2440 | public String call() throws Exception {
2441 | return body;
2442 | }
2443 | };
2444 | }
2445 | }
2446 | ```
2447 |
2448 | 你可以这样测试:
2449 |
2450 | ```java
2451 | given().
2452 | body("a string").
2453 | when().
2454 | async().post("/stringBody").
2455 | then().
2456 | body(equalTo("a string"));
2457 | ```
2458 |
2459 | 默认超时为1秒。也可以使用DSL更改超时:
2460 |
2461 | ```java
2462 | given().
2463 | body("a string").
2464 | when().
2465 | async().with().timeout(20, TimeUnit.SECONDS).post("/stringBody").
2466 | then().
2467 | body(equalTo("a string"));
2468 | ```
2469 | 还可以使用[AsyncConfig](http://static.javadoc.io/io.restassured/spring-mock-mvc/2.4.1/io/restassured/module/mockmvc/config/AsyncConfig.html)),例如:
2470 |
2471 | ```java
2472 | given().
2473 | config(config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS))).
2474 | body("a string").
2475 | when().
2476 | async().post("/stringBody").
2477 | then().
2478 | body(equalTo("a string"));
2479 | ```
2480 |
2481 |
2482 | `withTimeout`是从`io.restassured.module.mockmvc.config.AsyncConfig`静态导入的,只是创建一个具有给定超时的`AsyncConfig`的快捷方式。全局应用配置以应用于所有请求:
2483 |
2484 | ```java
2485 | RestAssuredMockMvc.config = RestAssuredMockMvc.config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS));
2486 |
2487 | // Request 1
2488 | given().
2489 | body("a string").
2490 | when().
2491 | async().post("/stringBody").
2492 | then().
2493 | body(equalTo("a string"));
2494 |
2495 | // Request 2
2496 | given().
2497 | body("another string").
2498 | when().
2499 | async().post("/stringBody").
2500 | then().
2501 | body(equalTo("a string"));
2502 | ```
2503 |
2504 | 请求1和2现在将使用默认超时100毫秒。
2505 |
2506 | ## 添加请求后处理器 ##
2507 | Spring MockMvc已经对[请求后处理器](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/RequestPostProcessor.html)做了支持,并且您也可以在RestAssuredMockMvc中使用。举个例子:
2508 | ```java
2509 | given().postProcessors(myPostProcessor1, myPostProcessor2). ..
2510 | ```
2511 |
2512 | 请注意,推荐从`org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors`(例如认证相关的 `RequestPostProcessors`)加入请求后处理器时,直接使用`auth`方法,这样可读性更强,当然结果是一样的:
2513 | ```java
2514 | given().auth().with(httpBasic("username", "password")). ..
2515 | ```
2516 | 这里的httpBasic静态导入自[SecurityMockMvcRequestPostProcessor](http://docs.spring.io/autorepo/docs/spring-security/current/apidocs/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.html)。
2517 |
2518 |
2519 |
2520 | ## 添加结果处理器 ##
2521 | Spring MockMvc对[结果处理器](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/ResultHandler.html) 做了支持,您也可以在RestAssuredMockMvc中使用。比方说您想要使用原生的MockMvc日志功能:
2522 |
2523 | ```java
2524 | .. .then().apply(print()). ..
2525 | ```
2526 |
2527 | 这里的`print`静态导入自`org.springframework.test.web.servlet.result.MockMvcResultHandlers`。请注意如果您正在使用2.6.0或更早版本的rest-assured,需要这样使用结果处理器:
2528 |
2529 | ```java
2530 | given().resultHandlers(print()). ..
2531 | ```
2532 | 但是rest-assured2.8.0起不推荐使用这种语法。
2533 |
2534 | ## 使用结果匹配器 ##
2535 | Spring MockMvc提供了许多[结果匹配器](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/ResultMatcher.html),您可以从中获益。RestAssuredMockMvc也对其中必要的功能进行支持。举个例子,基于某种原因您想要使用结果匹配器验证状态码是否等于200:
2536 | ```java
2537 | given().
2538 | param("name", "Johan").
2539 | when().
2540 | get("/greeting").
2541 | then().
2542 | assertThat(status().isOk()).
2543 | body("id", equalTo(1)).
2544 | body("content", equalTo("Hello, Johan!"));
2545 | ```
2546 | 这里的`status`静态导入自`org.springframework.test.web.servlet.result.MockMvcResultMatchers`。注意,您可以使用`expect`方法,功能上和`assertThat`一样,但是更接近原生的MockMvc的语法。
2547 |
2548 | ## 拦截器 ##
2549 | 您也可以在请求(译者注:这里指mock请求)曝光之前截住并改变[MockHttpServletRequestBuilder](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.html)。您需要先定义一个[MockHttpServletRequestBuilderInterceptor](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/intercept/MockHttpServletRequestBuilderInterceptor.html),并在RestAssuredMockMvc中使用:
2550 |
2551 | ```java
2552 | given().interceptor(myInterceptor). ..
2553 | ```
2554 |
2555 | ## Specifications ##
2556 |
2557 |
2558 | 正如标准的Rest Assured,你可以使用[specifications](#specification_re-use),以便更好地重用。请注意,RestAssuredMockMvc的请求规范构建器称为[MockMvcRequestSpecBuilder](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/specification/MockMvcRequestSpecBuilder.html)。同样的[ResponseSpecBuilder](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/builder/ResponseSpecBuilder.html) 也可以在RestAssuredMockMvc中使用。规格可以静态定义,就像标准的Rest Assured一样。例如:
2559 |
2560 | ```java
2561 | RestAssuredMockMvc.requestSpecification = new MockMvcRequestSpecBuilder().addQueryParam("name", "Johan").build();
2562 | RestAssuredMockMvc.responseSpecification = new ResponseSpecBuilder().expectStatusCode(200).expectBody("content", equalTo("Hello, Johan!")).build();
2563 |
2564 | given().
2565 | standaloneSetup(new GreetingController()).
2566 | when().
2567 | get("/greeting").
2568 | then().
2569 | body("id", equalTo(1));
2570 | ```
2571 |
2572 | ## 重置 RestAssuredMockMvc ##
2573 |
2574 | 如果您使用了任何静态配置,您可以通过调用RestAssuredMockMvc.reset()方法轻松地将RestAssuredMockMvc重置为其默认状态。
2575 |
2576 | ## Spring MVC认证 ##
2577 |
2578 | `spring-mock-mvc`的版本`2.3.0'支持身份验证。例如:
2579 |
2580 | ```java
2581 | given().auth().principal(..). ..
2582 | ```
2583 |
2584 | 一些认证方法需要Spring安全在类路径(可选)。也可以静态定义认证:
2585 |
2586 | ```java
2587 | RestAssuredMockMvc.authentication = principal("username", "password");
2588 | ```
2589 |
2590 | 其中`principal`方法是从[RestAssuredMockMvc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)静态导入的。还可以在请求构建器中定义认证方案:
2591 |
2592 | ```java
2593 | MockMvcRequestSpecification spec = new MockMvcRequestSpecBuilder.setAuth(principal("username", "password")).build();
2594 | ```
2595 |
2596 | ### 使用 Spring Security 测试 ###
2597 |
2598 | 从版本`2.5.0`也有更好的支持Spring Security。如果你在类路径中有`spring-security-test`,你可以这样做:
2599 |
2600 | ```java
2601 | given().auth().with(httpBasic("username", "password")). ..
2602 | ```
2603 |
2604 | 其中`httpBasic`是从[SecurityMockMvcRequestPostProcessor](http://docs.spring.io/autorepo/docs/spring-security/current/apidocs/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.html)静态导入的。这将对请求应用基本认证。为了这个工作,你需要应用[SecurityMockMvcConfigurer](http://docs.spring.io/autorepo/docs/spring-security/current/apidocs/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurers.html)到MockMvc实例。您可以手动执行此操作:
2605 |
2606 | ```java
2607 | MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).apply(SecurityMockMvcConfigurers.springSecurity()).build();
2608 | ```
2609 |
2610 | or RESTAssuredMockMvc will automatically try to apply the `springSecurity` configurer automatically if you initalize it with an instance of [AbstractMockMvcBuilder](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.html), for example when configuring a "web app context":
2611 | ```java
2612 | given().webAppContextSetup(context).auth().with(httpBasic("username", "password")). ..
2613 | ```
2614 |
2615 | Here's a full example:
2616 | ```java
2617 | import io.restassured.module.mockmvc.RestAssuredMockMvc;
2618 | import org.junit.After;
2619 | import org.junit.Before;
2620 | import org.junit.Test;
2621 | import org.junit.runner.RunWith;
2622 | import org.springframework.beans.factory.annotation.Autowired;
2623 | import org.springframework.test.context.ContextConfiguration;
2624 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
2625 | import org.springframework.test.context.web.WebAppConfiguration;
2626 | import org.springframework.web.context.WebApplicationContext;
2627 |
2628 | import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
2629 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
2630 | import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
2631 |
2632 | @RunWith(SpringJUnit4ClassRunner.class)
2633 | @ContextConfiguration(classes = MyConfiguration.class)
2634 | @WebAppConfiguration
2635 | public class BasicAuthExample {
2636 |
2637 | @Autowired
2638 | private WebApplicationContext context;
2639 |
2640 | @Before public void
2641 | rest_assured_is_initialized_with_the_web_application_context_before_each_test() {
2642 | RestAssuredMockMvc.webAppContextSetup(context);
2643 | }
2644 |
2645 | @After public void
2646 | rest_assured_is_reset_after_each_test() {
2647 | RestAssuredMockMvc.reset();
2648 | }
2649 |
2650 | @Test public void
2651 | basic_auth_example() {
2652 | given().
2653 | auth().with(httpBasic("username", "password")).
2654 | when().
2655 | get("/secured/x").
2656 | then().
2657 | statusCode(200).
2658 | expect(authenticated().withUsername("username"));
2659 | }
2660 | }
2661 | ```
2662 |
2663 | You can also define authentication for all request, for example:
2664 | ```java
2665 | RestAssuredMockMvc.authentication = with(httpBasic("username", "password"));
2666 | ```
2667 | where `with` is statically imported from `io.restassured.module.mockmvc.RestAssuredMockMvc`. It's also possible to use a [request specification](#specifications).
2668 |
2669 | ### 注入一个用户 ###
2670 |
2671 |
2672 | 也可以使用Spring Security测试注释,例如[@WithMockUser](http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#test-method-withmockuser)和 [@WithUserDetails](http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#test-method-withuserdetails)。例如,假设您想测试此控制器:
2673 |
2674 | ```java
2675 | @Controller
2676 | public class UserAwareController {
2677 |
2678 | @RequestMapping(value = "/user-aware", method = GET)
2679 | public
2680 | @ResponseBody
2681 | String userAware(@AuthenticationPrincipal User user) {
2682 | if (user == null || !user.getUsername().equals("authorized_user")) {
2683 | throw new IllegalArgumentException("Not authorized");
2684 | }
2685 |
2686 | return "Success");
2687 | }
2688 | }
2689 | ```
2690 |
2691 |
2692 | 您可以看到`userAware'方法需要一个 [User](http://docs.spring.io/autorepo/docs/spring-security/current/apidocs/org/springframework/security/core/userdetails/User.html) 作为参数,我们让Spring Security使用[@AuthenticationPrincipal](http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/web/bind/annotation/AuthenticationPrincipal.html) 注入它。要生成测试用户,我们可以这样做:
2693 |
2694 |
2695 | ```java
2696 | @WithMockUser(username = "authorized_user")
2697 | @Test public void
2698 | spring_security_mock_annotations_example() {
2699 | given().
2700 | webAppContextSetup(context).
2701 | when().
2702 | get("/user-aware").
2703 | then().
2704 | statusCode(200).
2705 | body(equalTo("Success")).
2706 | expect(authenticated().withUsername("authorized_user"));
2707 | }
2708 | ```
2709 |
2710 | 注意,也可以不使用注释,而是使用[RequestPostProcessor](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/RequestPostProcessor.html) ,例如[SecurityMockMvcRequestPostProcessors#user(java.lang.String)](http://docs.spring.io/autorepo/docs/spring-security/4.0.0.RELEASE/apidocs/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.html#user(java.lang.String)).
2711 |
2712 |
2713 | ## 参数相关 ##
2714 |
2715 | MockMvc没有区分不同类型的参数,所以`param`,`formParam`和`queryParam`目前只是委托给MockMvc中的param。 `formParam`自动添加`application / x-www-form-urlencoded`内容类型的头部,就像标准的Rest Assured一样。
2716 |
2717 | # Scala支持 #
2718 |
2719 | REST Assured 2.6.0引入了将“别名”添加到“then”方法的[scala-support](http://dl.bintray.com/johanhaleby/generic/scala-support-3.0.1-dist.zip)模块定义在[Response](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/response/Response.html)或[MockMvcResponse](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/response/MockMvcResponse.html)中调用“Then”。这样做的原因是`then`在将来可能是Scala中的保留关键字,并且当使用具有此名称的方法时,编译器会发出警告。要启用`Then`,只需从`scala-support`模块导入`io.restassured.module.scala.RestAssuredSupport.AddThenToResponse`类。例如:
2720 |
2721 |
2722 | ```java
2723 | import io.restassured.RestAssured.when
2724 | import io.restassured.module.scala.RestAssuredSupport.AddThenToResponse
2725 | import org.hamcrest.Matchers.equalTo
2726 | import org.junit.Test
2727 |
2728 | @Test
2729 | def `trying out rest assured in scala with implicit conversion`() {
2730 | when().
2731 | get("/greetJSON").
2732 | Then().
2733 | statusCode(200).
2734 | body("key", equalTo("value"))
2735 | }
2736 | ```
2737 | 注意:同样支持 [Spring Mock Mvc Module](#spring-mock-mvc-module).
2738 |
2739 |
2740 | 可以像这样使用它:
2741 |
2742 | #### SBT:
2743 | ```scala
2744 | libraryDependencies += "io.rest-assured" % "scala-support" % "3.0.1"
2745 | ```
2746 |
2747 | #### Maven:
2748 | ```xml
2749 |
2750 | io.rest-assured
2751 | scala-support
2752 | 3.0.1
2753 | test
2754 |
2755 | ```
2756 |
2757 | #### Gradle:
2758 | ```xml
2759 | testCompile 'io.rest-assured:scala-support:3.0.1'
2760 | ```
2761 |
2762 | ### No build manager:
2763 | 手动下载 [distribution file](http://dl.bintray.com/johanhaleby/generic/scala-support-3.0.1-dist.zip) 。
2764 |
2765 | # Kotlin支持 #
2766 |
2767 | Kotlin是由[JetBrains](https://www.jetbrains.com/)开发的一种语言,它与Java和REST Assured非常好地集成。当使用它与REST Assured有一件事,必须逃避`when`,因为它是Kotlin中的保留关键字。例如:
2768 |
2769 | ```kotlin
2770 | Test fun kotlin_rest_assured_example() {
2771 | given().
2772 | param("firstName", "Johan").
2773 | param("lastName", "Haleby").
2774 | `when`().
2775 | get("/greeting").
2776 | then().
2777 | statusCode(200).
2778 | body("greeting.firstName", equalTo("Johan")).
2779 | body("greeting.lastName", equalTo("Haleby"))
2780 | }
2781 | ```
2782 |
2783 |
2784 | 为了解决这个问题,创建一个[extension function](https://kotlinlang.org/docs/reference/extensions.html),创建一个别名为`when`时叫做`When`:
2785 |
2786 | ```kotlin
2787 | fun RequestSpecification.When(): RequestSpecification {
2788 | return this.`when`()
2789 | }
2790 | ```
2791 |
2792 | 代码现在可以像这样写:
2793 |
2794 | ```kotlin
2795 | Test fun kotlin_rest_assured_example() {
2796 | given().
2797 | param("firstName", "Johan").
2798 | param("lastName", "Haleby").
2799 | When().
2800 | get("/greeting").
2801 | then().
2802 | statusCode(200).
2803 | body("greeting.firstName", equalTo("Johan")).
2804 | body("greeting.lastName", equalTo("Haleby"))
2805 | }
2806 | ```
2807 |
2808 | 注意,我们不需要任何转义。有关更多详细信息,请参阅[this](http://code.haleby.se/2015/11/06/rest-assured-with-kotlin/)博客文章。
2809 |
2810 | # 更多信息 #
2811 | 其他相关信息,请参考 [javadoc](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/index.html):
2812 | * [RestAssured](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/RestAssured.html)
2813 | * [RestAssuredMockMvc Javadoc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)
2814 | * [Specification package](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/specification/package-summary.html)
2815 |
2816 | 一些代码示例:
2817 | * REST Assured [tests](https://github.com/rest-assured/rest-assured/tree/master/examples/rest-assured-itest-java/src/test/java/io/restassured/itest/java)
2818 | * [JsonPathTest](https://github.com/rest-assured/rest-assured/blob/master/json-path/src/test/java/io/restassured/path/json/JsonPathTest.java)
2819 | * [XmlPathTest](https://github.com/rest-assured/rest-assured/blob/master/xml-path/src/test/java/io/restassured/path/xml/XmlPathTest.java)
2820 |
2821 | 如果你需要支持,可以加入 [mailing list](http://groups.google.com/group/rest-assured).
2822 |
2823 | 如果需要专业支持,请联系 [johanhaleby](https://github.com/johanhaleby).
2824 |
--------------------------------------------------------------------------------