└── 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 | matchesXsdmatchesDtd方法在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 |
1311 | 1312 | 1313 | 1314 | 1315 |
User: 
Password:
1316 |
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 |
1455 | 1456 | 1457 |
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 | 正如标准的Res​​t 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中使用。规格可以静态定义,就像标准的Res​​t 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`内容类型的头部,就像标准的Res​​t 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 | --------------------------------------------------------------------------------