rest-assured 使用指南

本帖已被设为精华帖!,

原文:https://github.com/rest-assured/rest-assured/wiki/Usage
本文github地址:https://github.com/RookieTester/rest-assured-doc

注意,如果您正在使用1.9.0或者更早的版本请参考旧文档。

REST Assured是一个可以简化HTTP Builder顶层 基于REST服务的测试过程的Java DSL(针对某一领域,具有受限表达性的一种计算机程序设计语言)。它支持发起POST,GET,PUT,DELETE,OPTIONS,PATCH和HEAD请求,并且可以用来验证和校对这些请求的响应信息。

目录

  1. 静态导入方法
  2. 示例
    1. JSON 示例
    2. JSON Schema Validation
    3. XML 示例
    4. 高级用法
    5. XML
    6. JSON
    7. 其它示例
  3. 关于float和double
  4. 语法关注点 (语法糖)
  5. 获得响应体信息
    1. 从已验证的响应体中提取值
    2. JSON (使用 JsonPath)
    3. XML (使用XmlPath)
    4. 获取某个路径下的值
    5. Headers, cookies, status等
    6. 获取全部header值
    7. 获取全部cookie值
    8. 获取详细的cookie值
  6. 获得响应信息
    1. 在验证响应之后提取特定的值
    2. JSON (使用JsonPath)
    3. XML (使用XmlPath)
    4. 单独使用路径
    5. Headers, cookies, status等
    6. 获取header
    7. 获取cookie
    8. 获取详细的cookie值
  7. 指定请求数据
    1. 请求HTTP资源
    2. 参数化
    3. 多值参数
    4. 参数不赋值
    5. 路径参数
    6. Cookie
    7. Header
    8. Content-Type
    9. 请求正文
  8. 验证响应信息
    1. 响应体
    2. Cookie
    3. 状态码
    4. Header
    5. Content-Type
    6. 内容全匹配
    7. 关联类型验证
    8. 计算响应时间
  9. 认证
    1. 基本认证
    2. 抢占式的基本认证
    3. 受质询的基本认证
    4. 摘要认证
    5. 表单认证
    6. CSRF
    7. OAuth
    8. OAuth1
    9. OAuth2
  10. Multi-part类型的表单数据
  11. 对象映射
    1. 序列化
    2. 基于Content-Type的序列化
    3. 由HashMap创建JSON
    4. 使用显式序列化器
    5. 反序列化
    6. 基于Content-Type的反序列化
    7. 自定义content-type的反序列化
    8. 使用显式反序列化器
    9. 配置
    10. 自定义
  12. 解析器
    1. 自定义解析器
    2. 默认解析器
  13. 默认值
  14. 模式复用
  15. 过滤器
    1. Response Builder
  16. 日志
    1. 请求日志
    2. 响应日志
    3. 认证失败日志
  17. 根路径
    1. 路径参数
  18. Session支持
    1. Session过滤器
  19. SSL
    1. SSL无效的主机名
  20. URL编码
  21. 代理(proxy)配置
    1. 静态代理配置
    2. 请求规范代理配置
  22. 详细配置
    1. 编码配置
    2. 解码配置
    3. Session配置
    4. 重定向(Redirect) DSL
    5. 网络连接配置
    6. JSON配置
    7. HTTP客户端配置
    8. SSL配置
    9. 参数配置
  23. Spring Mock Mvc模型
    1. Bootstrapping RestAssuredMockMvc
    2. 异步请求
    3. 添加Request Post Processors
    4. 添加Result Handlers
    5. 使用Result匹配器
    6. 拦截器
    7. Specifications
    8. 重置 RestAssuredMockMvc
    9. Spring MVC 身份认证
      1. 使用 Spring Security 测试
      2. 注入一个用户
    10. 参数相关
  24. Scala支持
  25. Kotlin支持
  26. 更多

静态导入方法

推荐大家从以下的类中静态导入方法,以提高使用rest-assured的效率。

io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*

如果您想使用Json Schema validation 还应该静态导入这些方法:

io.restassured.module.jsv.JsonSchemaValidator.*

更多使用方法参阅 Json Schema Validation 。

如果您正在使用SpringMVC,你可以使用spring-mock-mvc 模型的Rest Assured DSL来对Spring的controller层进行单元测试。为此需要从RestAssuredMockMvc静态导入这些方法,而不是io.restassured.RestAssured:

io.restassured.module.mockmvc.RestAssuredMockMvc.*

示例

例一 – JSON

假设某个get请求 (to http://localhost:8080/lotto) 返回JSON如下:

{
"lotto":{
 "lottoId":5,
 "winning-numbers":[2,45,34,23,7,5,3],
 "winners":[{
   "winnerId":23,
   "numbers":[2,45,34,23,3,5]
 },{
   "winnerId":54,
   "numbers":[52,3,12,11,18,22]
 }]
}
}

REST assured可以帮您轻松地进行get请求并对响应信息进行处理。举个例子,如果想要判断lottoId的值是否等于5,你可以这样做:

get("/lotto").then().body("lotto.lottoId", equalTo(5));

又或许您想要检查winnerId的取值包括23和54:

get("/lotto").then().body("lotto.winners.winnerId", hasItems(23, 54));

注意: equalTohasItems 是 Hamcrest matchers的方法,所以需要静态导入 org.hamcrest.Matchers

注意这里的”json path”语法使用的是Groovy的GPath标注法,不要和Jayway的JsonPath语法混淆。

以BigDecimal返回float和double类型

(译者注:Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算)

您可以对rest-assured和JsonPath进行配置,使之以BigDecimal返回json里的数值类型数据,而不是float或者double。可以参考下面json文本:

{

    "price":12.12 

}

默认情况下您验证price字段是否等于float类型的12.12像这样:

get("/price").then().body("price", is(12.12f));

但是如果想用rest-assured的JsonConfig来配置返回的所有的json数值都为BigDecimal类型:

given().
        config(RestAssured.config().jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))).
when().
        get("/price").
then().
        body("price", is(new BigDecimal(12.12));

JSON Schema validation

自从 2.1.0 版本rest-assured开始支持Json Schema validation. 举个例子,在classpath中放置以下的schema文件(译者注:idea的话可以放在resources目录下),products-schema.json:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product set",
    "type": "array",
    "items": {
        "title": "Product",
        "type": "object",
        "properties": {
            "id": {
                "description": "The unique identifier for a product",
                "type": "number"
            },
            "name": {
                "type": "string"
            },
            "price": {
                "type": "number",
                "minimum": 0,
                "exclusiveMinimum": true
            },
            "tags": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "minItems": 1,
                "uniqueItems": true
            },
            "dimensions": {
                "type": "object",
                "properties": {
                    "length": {"type": "number"},
                    "width": {"type": "number"},
                    "height": {"type": "number"}
                },
                "required": ["length", "width", "height"]
            },
            "warehouseLocation": {
                "description": "Coordinates of the warehouse with the product",
                "$ref": "http://json-schema.org/geo"
            }
        },
        "required": ["id", "name", "price"]
    }
}

您可以使用这个schema验证(/products)这个请求是否符合规范:

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));

matchesJsonSchemaInClasspath 静态导入自 io.restassured.module.jsv.JsonSchemaValidator 并且我们推荐从这个类中静态导入所有的方法。然而为了使用它需要依赖于json-schema-validator module 或者从这个网页 下载 它, 又或者通过maven添加下面的依赖:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>json-schema-validator</artifactId>
    <version>3.0.1</version>
</dependency>

JSON Schema Validation 设置项

rest-assured的json-schema-validator module使用Francis Galiegue的json-schema-validator (fge) 库来进行验证。 如果您想配置使用基础fge库,你可以像下面例子中:

// Given
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV4).freeze()).freeze();

// When
get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(jsonSchemaFactory));

using方法允许您进入jsonSchemaFactory的实例,rest-assured在验证期间也会进行此操作。这种方式允许我们对验证进行细粒度的配置。

fge库也允许验证状态是 checked或者unchecked(译者注:表示不懂)。默认情况,rest-assured使用checked验证,但是如果你想要改变这种方式,您可以提供一个matcher的JsonSchemaValidatorSettings实例。举个例子:

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(settings().with().checkedValidation(false)));

这些settings方法静态导入自 JsonSchemaValidatorSettings类。

Json Schema Validation的静态配置

现在想象下您总是使用unchecked验证,并且设置默认的json schema版本为3。与其每次都在代码里进行设置,不如静态地进行定义设置。举个例子:

JsonSchemaValidator.settings = settings().with().jsonSchemaFactory(
        JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV3).freeze()).freeze()).
        and().with().checkedValidation(false);

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));

现在任意一个由JsonSchemaValidator导入的matcher都会使用DRAFTV3作为默认版本并且unchecked validation。

想要重置JsonSchemaValidator到默认设置仅仅需要调用reset方法:

JsonSchemaValidator.reset();

不使用rest-assured的Json Schema Validation

您也可以在不依赖rest-assured的情况下使用json-schema-validator module。如想要把json文本表示为String类型的字符串,可以这样做:

import org.junit.Test;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import static org.hamcrest.MatcherAssert.assertThat;

public class JsonSchemaValidatorWithoutRestAssuredTest {


    @Test 
    public void validates_schema_in_classpath() {
        // Given
        String json = ... // Greeting response

        // Then
        assertThat(json, matchesJsonSchemaInClasspath("greeting-schema.json"));
    }
}

更多信息请参阅新手入门。

匿名式的JSON根节点验证

一个JSON文本并不总是有一个命名好的根属性。这里有个验证这种JSON的例子:

[1, 2, 3]

一个匿名的JSON根属性可以通过使用$或者空字符串作为路径来识别。举个例子,通过访问http://localhost:8080/json这个地址可以获得一个JSON文本,我们可以使用rest-assured验证:

when().
     get("/json").
then().
     body("$", hasItems(1, 2, 3)); // An empty string "" would work as well

例2 – XML

XML可以一种通过简单的方式解析。假设一个POST请求http://localhost:8080/greetXML返回:

<greeting>
   <firstName>{params("firstName")}</firstName>
   <lastName>{params("lastName")}</lastName>
</greeting>

换言之,它在请求中返还了一个基于firstname和lastname请求参数的greeting节点。您可以通过rest-assured轻易地展现和解析这个例子:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).

如果您想同时解析firstname和lastname可以这样做:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).
         body("greeting.lastName", equalTo("Doe"));

或者稍微简短些:

with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body("greeting.firstName", equalTo("John"), "greeting.lastName", equalTo("Doe"));

看这里 的链接获取有关语法的更多信息(它遵循 Groovy的 GPath 语法).

XML 命名空间

考虑到您需要使用io.restassured.config.XmlConfig声明一个命名空间。举个例子,有一个位于http://localhost:8080的资源namespace-example,返回如下的XML:

<foo xmlns:ns="http://localhost/">
  <bar>sudo </bar>
  <ns:bar>make me a sandwich!</ns:bar>
</foo>

可以然后声明http://localhost/这个URI并且验证其响应:

given().
        config(RestAssured.config().xmlConfig(xmlConfig().declareNamespace("test", "http://localhost/"))).
when().
         get("/namespace-example").
then().
         body("foo.bar.text()", equalTo("sudo make me a sandwich!")).
         body(":foo.:bar.text()", equalTo("sudo ")).
         body("foo.test:bar.text()", equalTo("make me a sandwich!"));

这个路径语法遵循Groovy的XmlSlurper语法。注意直到2.6.0的路径语法都遵循Groovy的XmlSlurper语法。请看release notes可以获知2.6.0之前的版本语法是怎样的。

XPath

您也可以使用x-path来解析XML响应。举个例子:

given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body(hasXPath("/greeting/firstName", containsString("Jo")));

或者

given().parameters("firstName", "John", "lastName", "Doe").post("/greetXML").then().body(hasXPath("/greeting/firstName[text()='John']"));

在XPath表达式中使用命名空间,你需要在配置中启用这些选项:

given().
        config(RestAssured.config().xmlConfig(xmlConfig().with().namespaceAware(true))).
when().
         get("/package-db-xml").
then().
         body(hasXPath("/db:package-database", namespaceContext));

namespaceContext 是一个javax.xml.namespace.NamespaceContext的实例 。

Schema和DTD

XML响应体也可以验证为一个XML Schema (XSD)或DTD.

XSD 例子

get("/carRecords").then().assertThat().body(matchesXsd(xsd));

DTD 例子

get("/videos").then().assertThat().body(matchesDtd(dtd));

matchesXsd
matchesDtd方法在Hamcrest matchers里,你可以从io.restassured.matcher.RestAssuredMatchers

导入。

例3 – 复杂的解析和验证

这正是rest-assured闪光点所在!由于rest-assured实现了Groovy,它可以从Groovy集合的API的优点中获益。让我们从下面的Groovy例子中开始探索:

def words = ['ant', 'buffalo', 'cat', 'dinosaur']
def wordsWithSizeGreaterThanFour = words.findAll { it.length() > 4 }

在第一行,我们简单地定义了一个包含一些单词的列表,不过第二行更加有趣。
这里我们检索了列表里的所有长度大于4的单词,通过一个叫做findAll的Groovy闭包。
这个闭包有一个内部变量it,代表着列表中当前的元素。
结果是一个新的列表, wordsWithSizeGreaterThanFour,包含buffalo and dinosaur

这里还有一些其它的有趣的方法,我们也可以使用在Groovy集合中:

  • find – 找到第一个匹配闭包谓词(closure predicate)的元素
  • collect – 收集在集合里的每个元素都调用的闭包返回值(collect the return value of calling a closure on each item in a collection)
  • sum – 对集合里的元素进行求和
  • max/min – 返回集合里的最大值/最小值

所以我们如何在使用rest-assured验证XML和JSON响应时利用这些优点?

XML示例

比方说我们有个资源http://localhost:8080/shopping返回如下的XML:

<shopping>
      <category type="groceries">
        <item>Chocolate</item>
        <item>Coffee</item>
      </category>
      <category type="supplies">
        <item>Paper</item>
        <item quantity="4">Pens</item>
      </category>
      <category type="present">
        <item when="Aug 10">Kathryn's Birthday</item>
      </category>
</shopping>

又比如我们想写一个测试来检验类型为groceries的category节点有Chocolate和Coffee这两个项目。在rest-assured可以这样做:

when().
       get("/shopping").
then().
       body("shopping.category.find { it.@type == 'groceries' }.item", hasItems("Chocolate", "Coffee"));

这里发生了什么事?首先使用XML路径shopping.category获取了所有categoriy的一个列表。在这个列表中我们又调用了一个方法,find,来返回有type这个属性且该属性值为groceries的单个category节点。

在这个category上我们接下来继续收集所有相关联的项目(item)。

由于这里与category相关联的项目不止一个,所以会返回一个列表。接下来我们通过Hamcrest matcher的hasItems方法来解析它。

但是如果我们想取得一些项目(item)但又不想进行断言验证该怎么办?您可以参考XmlPath:

// Get the response body as a String
String response = get("/shopping").asString();
// And get the groceries from the response. "from" is statically imported from the XmlPath class
List<String> groceries = from(response).getList("shopping.category.find { it.@type == 'groceries' }.item");

如果groceries是您对这个响应里唯一的关注点,也可以使用一个捷径:

// Get the response body as a String
List<String> groceries = get("/shopping").path("shopping.category.find { it.@type == 'groceries' }.item");

深度优先搜索

实际上之前的例子我们还可以继续简化:

when().
       get("/shopping").
then().
       body("**.find { it.@type == 'groceries' }", hasItems("Chocolate", "Coffee"));

**是一种在XML文件中做深度优先搜索的捷径。

我们搜索第一个type属性值等于”groceries”的节点。注意我们没有在”item”这个XML路径结束。

原因是在category节点返回一个列表的项目值时,自动调用了toString()这个方法(译者注:这两句有啥因果关系我没搞懂)。

JSON示例

假设http://localhost:8080/store返回如下的JSON:

{  
   "store":{  
      "book":[  
         {  
            "author":"Nigel Rees",
            "category":"reference",
            "price":8.95,
            "title":"Sayings of the Century"
         },
         {  
            "author":"Evelyn Waugh",
            "category":"fiction",
            "price":12.99,
            "title":"Sword of Honour"
         },
         {  
            "author":"Herman Melville",
            "category":"fiction",
            "isbn":"0-553-21311-3",
            "price":8.99,
            "title":"Moby Dick"
         },
         {  
            "author":"J. R. R. Tolkien",
            "category":"fiction",
            "isbn":"0-395-19395-8",
            "price":22.99,
            "title":"The Lord of the Rings"
         }
      ]
   }
}

例1

在本例中我们发起一个请求”/store”,并且做了一个断言:搜集满足price字段值小于10的所有book数组里的title字段,得到了”Sayings of the Century”和”Moby Dick”这两个结果:

when().
       get("/store").
then().
       body("store.book.findAll { it.price < 10 }.title", hasItems("Sayings of the Century", "Moby Dick"));

就像上面XML的例子,我们使用闭包获取所有price字段值低于10的book数组,并且返回相应的title字段值集合。

然后使用hasItems这个匹配器来断言得到我们预期的结果。使用JsonPath 我们可以用下面的方法替代:

// Get the response body as a String
String response = get("/store").asString();
// And get all books with price < 10 from the response. "from" is statically imported from the JsonPath class
List<String> bookTitles = from(response).getList("store.book.findAll { it.price < 10 }.title");

例2

考虑下该如何断言所有author字段值长度总和是否大于50的结果。

这是个挺难以回答的问题,也正展示了闭包和Groovy集合的强大之处。在rest-assured里可以:

when().
       get("/store");
then().
       body("store.book.author.collect { it.length() }.sum()", greaterThan(50));

首先我们通过(store.book.author)得到了所有的author字段值,然后使用闭包里的方法{ it.length() }解析这个集合。

它所做的是对列表里的每一个author字段执行一次length()方法,然后返回一个新的列表。在这个列表中,我们再调用sum()方法来求得字符长度的总和。

最终的结果是53,并且我们使用greaterThan匹配器的断言结果是大于50 。
但是实际上可以继续简化这种写法。可以再次参考”words”这个例子

def words = ['ant', 'buffalo', 'cat', 'dinosaur']

Groovy有一个便利的方法可以遍历列表中的所有元素,使用*来调用。举个例子:

def words = ['ant', 'buffalo', 'cat', 'dinosaur']
assert [3, 6, 3, 8] == words*.length()

Groovy返回了一个新的包含words中每个字段字符长度的列表。我们也可以把rest-assured中的这个语法用在author列表中:

when().
       get("/store");
then().
       body("store.book.author*.length().sum()", greaterThan(50)).

当然我们可以使用JsonPath来获取这个结果:

// Get the response body as a string
String response = get("/store").asString();
// Get the sum of all author length's as an int. "from" is again statically imported from the JsonPath class
int sumOfAllAuthorLengths = from(response).getInt("store.book.author*.length().sum()");
// We can also assert that the sum is equal to 53 as expected.
assertThat(sumOfAllAuthorLengths, is(53));

其它例子

Micha Kops曾写过一篇很优秀的博客,里面包含大量示例(包括可检出的代码)。您可以由此进入试读。

Bas Dijkstra也开展过不少关于rest-assured的开源研究和资源。你可以由此进入试读,如果您想试用或者作出贡献,他的github仓库里有些可以尝试的练习题。

关于float和double

浮点型数字必须和Java的基本类型”float”区分开。举个例子,如果我们看下面的JSON对象:

{

    "price":12.12 

}

如下的测试将会失败,因为我们在拿一个”double”在比较,而不是”float”:

get("/price").then().assertThat().body("price", equalTo(12.12));

想用”float”比较的话写法应该是:

get("/price").then().assertThat().body("price", equalTo(12.12f));

语法关注点

当阅读rest-assured的博客时,你也许会看到许多使用”given / expect / when”语法的例子,举个例子:

given().
        param("x", "y").
expect().
        body("lotto.lottoId", equalTo(5)).
when().
        get("/lotto");

这是一种“遗留语法”,这实际上是rest-assured 1.x.版本用来写测试用例的方式。然而这种运作方式令许多用户迷惑甚至恼怒。这是因为一开始没有把”given / when / then”作为主要的技术来使用。所以rest-assured得2.0版本之前差不多不支持这种类似BDD-like测试的标准用法。”given / expect / when”在2.0仍然可用但是”given / when / then”可读性更强所以在测试用例中更为推荐。然而使用”given / expect / when”还有一个好处,就是所有的期望中的错误可以在同时展示出来,这是新语法做不到的(自从预期结果放在了最后面)。这意味着如果你有多个预期结果想要检验你可以:

given().
        param("x", "y").
expect().
        statusCode(400).
        body("lotto.lottoId", equalTo(6)).
when().
        get("/lotto");

rest-assured将同时报告状态码预期和响应体预期结果都是错的。将这些用新语法重写:

given().
        param("x", "y").
when().
        get("/lotto").
then().
        statusCode(400).
        body("lotto.lottoId", equalTo(6));

将会仅仅报告首个预期/断言失败的内容(比如预期状态码是400实际是200),第二个断言将不执行。您将不得不重新运行这个用例以期获取到第二个断言的结果。

语法糖

rest-assured中另一件值得注意的是,有些语法仅仅存在于语法糖中,举个例子,”and”在一行代码中使用可以增强可读性。

given().param("x", "y").and().header("z", "w").when().get("/something").then().assertThat().statusCode(200).and().body("x.y", equalTo("z"));

这等价于:

given().
        param("x", "y").
        header("z", "w").
when().
        get("/something").
then().
        statusCode(200).
        body("x.y", equalTo("z"));

获得响应体信息

你也可以获得响应的内容。比方说你想通过发起一个get请求”/lotto”并获取其响应内容。你可以以多种方式:

InputStream stream = get("/lotto").asInputStream(); // Don't forget to close this one when you're done
byte[] byteArray = get("/lotto").asByteArray();
String json = get("/lotto").asString();

从已验证的响应体中提取值

您可以从响应信息中提取值,或者使用extract方法仅仅返回response本身的一个实例。如何你想获取响应里的值,并将其作为接下来的请求内容,这会很有用。下面是一个叫做title的资源返回的JSON数据:

{
    "title" : "My Title",
     "_links": {
             "self": { "href": "/title" },
             "next": { "href": "/title?page=2" }
          }
}

想验证内容类型是JSON格式且标题是My Title,但是还想要从中提取next的值并用来发起请求,下面是使用方法:

String nextTitleLink =
given().
        param("param_name", "param_value").
when().
        get("/title").
then().
        contentType(JSON).
        body("title", equalTo("My Title")).
extract().
        path("_links.next.href");

get(nextTitleLink). ..

如果您想提取多个值,也可以考虑返回整个响应体:

Response response = 
given().
        param("param_name", "param_value").
when().
        get("/title").
then().
        contentType(JSON).
        body("title", equalTo("My Title")).
extract().
        response(); 

String nextTitleLink = response.path("_links.next.href");
String headerValue = response.header("headerName");

JSON (使用 JsonPath)

一旦我们取得了响应体,可以使用JsonPath来提取相应的数据:

int lottoId = from(json).getInt("lotto.lottoId");
List<Integer> winnerIds = from(json).get("lotto.winners.winnerId");

或者更高效一些:

JsonPath jsonPath = new JsonPath(json).setRoot("lotto");
int lottoId = jsonPath.getInt("lottoId");
List<Integer> winnerIds = jsonPath.get("winners.winnderId");

注意这里我们独立地使用了JsonPath,而没有依赖rest-assured本身的功能,看getting started guide 获取更多信息。

JsonPath 配置

您可以为JsonPath配置反序列化对象(object de-serializers),举个例子:

JsonPath jsonPath = new JsonPath(SOME_JSON).using(new JsonPathConfig("UTF-8"));

也可以静态配置好JsonPath,这样所有的JsonPath实例都会共享这个配置:

JsonPath.config = new JsonPathConfig("UTF-8");

更多JsonPath的内容参照这篇博客。

注意这里的JsonPath基于Groovy的GPath,不要和Jayway的搞混了。

XML (使用XmlPath)

您也可以使用XmlPath相应的功能:

String xml = post("/greetXML?firstName=John&lastName=Doe").andReturn().asString();
// Now use XmlPath to get the first and last name
String firstName = from(xml).get("greeting.firstName");
String lastName = from(xml).get("greeting.firstName");

// or a bit more efficiently:
XmlPath xmlPath = new XmlPath(xml).setRoot("greeting");
String firstName = xmlPath.get("firstName");
String lastName = xmlPath.get("lastName");

注意,您可以独立于rest-assured,单独使用XmlPath的功能,更多信息参见getting started guide。

XmlPath配置

你可以配置XmlPath的对象反序列化器和字符编码,举个例子:

XmlPath xmlPath = new XmlPath(SOME_XML).using(new XmlPathConfig("UTF-8"));

也可以静态地配置XmlPath,使得所有的实例都能共享这套配置:

XmlPath.config = new XmlPathConfig("UTF-8");

更多关于XmlPath的信息参阅这篇博客。

获取某个路径下的值

如您你只是想发起一个请求并返回一个路径下的值,你可以使用一个捷径:

int lottoId = get("/lotto").path("lotto.lottoid");

rest-assured会基于响应体的content-type自动决定是使用JsonPath还是XmlPath。如果这个类型在rest-assured没有被定义,它将会自动到default parser中查找。你可以自行(代码指定)决定使用哪种,比如:

String firstName = post("/greetXML?firstName=John&lastName=Doe").andReturn().xmlPath().getString("firstName");

xmlPath, jsonPathhtmlPath都是可选项。

Headers, cookies, status等

您也可以获取 header, cookie, 状态行,状态码:

Response response = get("/lotto");

// 获取所有 headers 信息
Headers allHeaders = response.getHeaders();

// 获取单个 header 信息
String headerName = response.getHeader("headerName");

// 获取所有 cookie 键值对
Map<String, String> allCookies = response.getCookies();

// 获取单个 cookie 信息
String cookieValue = response.getCookie("cookieName");

// 获取状态行信息
String statusLine = response.getStatusLine();

// 获取状态码信息
int statusCode = response.getStatusCode();

header 和 cookie 可以包含同名的多个值。

多个 header

要获取header的所有值,您需要首先从Response对象中获取Headers 对象。您需要首先从Response对象中获取Headers对象。您可以使用Headers.getValues(
)方法返回一个具有所有header值的List列表。

要获取cookie的所有值,您需要首先从Response对象中获取Cookie对象。您可以使用Cookie.getValues()方法获取所有值,该方法返回包含所有Cookie值的List列表。

详细的 Cookies 信息

如果您需要获取Cookie的路径或过期日期等详细信息,您需要从REST Assured获取一个detailed cookie。您可以使用Response.getDetailedCookie(java.lang.String) 方法获取单个Cookie,包括与给定名称相关联的所有属性。

您还可以使用Response.getDetailedCookies()方法获取所有详细的响应cookies。

指定请求数据

除了指定请求参数,您还可以指定header,Cookie,正文和Content Type。

请求HTTP资源

您通常通过调用request specification中的“HTTP方法”执行请求。例如:

when().get("/x"). ..;

其中get是HTTP请求方法。

从REST Assured 3.0.0开始,您可以通过使用该方法为请求使用任何HTTP动词。

when().
       request("CONNECT", "/somewhere").
then().
       statusCode(200);

这将向服务器发送“连接”请求。

参数化

通常您可以这样指定参数:

given().
       param("param1", "value1").
       param("param2", "value2").
when().
       get("/something");

REST Assured将自动尝试基于HTTP方法确定哪个参数类型(即查询或表单参数)。在GET的情况下,查询参数将被自动使用,在POST的情况下将使用表单参数。在某些情况下,重要的是在PUT或POST中分离表单和查询参数。你可以这样使用:

given().
       formParam("formParamName", "value1").
       queryParam("queryParamName", "value2").
when().
       post("/something");

参数也可以url上进行设置:

..when().get("/name?firstName=John&lastName=Doe");

参数如果上传的是文件,字节数组,输入流或文本的可以参照Multi-part类型的表单数据部分

多值参数

多值参数是每个参数名称具有多于一个值的参数(即,每个名称的值的列表)。您可以使用var-args指定这些值:

given().param("myList", "value1", "value2"). .. 

或者使用 list 列表:

List<String> values = new ArrayList<String>();
values.add("value1");
values.add("value2");

given().param("myList", values). .. 

无值参数

您还可以指定一个没有值的请求或表单参数:

given().param("paramName"). ..

路径参数

您还可以在请求中指定所谓的路径参数,例如

post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);

这些种类的路径参数在REST Assured中称为“未命名路径参数”,因为它们是基于索引的(hotelId将等于“My Hotel”,因为它是第一个占位符)。

您还可以使用命名路径参数:

given().
        pathParam("hotelId", "My Hotel").
        pathParam("roomNumber", 23).
when(). 
        post("/reserve/{hotelId}/{roomNumber}").
then().
         ..

路径参数使得更容易读取请求路径,且使请求路径能够在具有不同参数值的许多测试中容易地重复使用。

从版本2.8.0开始,您可以混合未赋值和赋值好的路径参数:

given().
        pathParam("hotelId", "My Hotel").        
when(). 
        post("/reserve/{hotelId}/{roomNumber}", 23).
then().
         ..

这里 roomNumber 的值My Hotel将被替换为 23.

注意,指定太少或太多的参数将导致错误消息。对于高级用例,您可以从[过滤器](#过滤器)添加,更改,删除(甚至冗余的路径参数)。

通常模式下,您可以通过以下方法指定Cookie:

given().cookie("username", "John").when().get("/cookie").then().body(equalTo("username"));

也可以像这样给cookie指定多个值:

given().cookie("cookieName", "value1", "value2"). ..

这将创建两个cookie:cookieName = value1和cookieName = value2。

您还可以使用以下方式指定详细的Cookie:

Cookie someCookie = new Cookie.Builder("some_cookie", "some_value").setSecured(true).setComment("some comment").build();
given().cookie(someCookie).when().get("/cookie").then().assertThat().body(equalTo("x"));

或同时指定cookies:

Cookie cookie1 = Cookie.Builder("username", "John").setComment("comment 1").build();
Cookie cookie2 = Cookie.Builder("token", 1234).setComment("comment 2").build();
Cookies cookies = new Cookies(cookie1, cookie2);
given().cookies(cookies).when().get("/cookie").then().body(equalTo("username, token"));
given().header("MyHeader", "Something").and(). ..
given().headers("MyHeader", "Something", "MyOtherHeader", "SomethingElse").and(). ..

也可以给一个headers指定多个值:

given().header("headerName", "value1", "value2"). ..

这将创建两个header,headerName = value1和headerName = value2

Header 合并/覆盖

默认情况下,header合并可以这样:

given().header("x", "1").header("x", "2"). ..

请求将包含两个标头,“x:1”和“x:2”。您可以在[HeaderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/HeaderConfig.html)的基础上进行更改。例如:

given().
        config(RestAssuredConfig.config().headerConfig(headerConfig().overwriteHeadersWithName("x"))).
        header("x", "1").
        header("x", "2").
when().
        get("/something").
...

这意味着只有header “x = 2”被发送到服务器

Content Type

given().contentType(ContentType.TEXT). ..
given().contentType("application/json"). ..

请求正文

given().body("some body"). .. // Works for POST, PUT and DELETE requests
given().request().body("some body"). .. // More explicit (optional)
given().body(new byte[]{42}). .. // Works for POST, PUT and DELETE
given().request().body(new byte[]{42}). .. // More explicit (optional)

您还可以将Java对象序列化为JSON或XML。点击这里了解详情。

验证响应数据

您还可以验证状态码,状态行,Cookie,headers,内容类型和正文。

响应体

请参阅使用示例,例如JSON 或 XML.

您还可以将响应正文映射到Java对象,单击这里 了解详细信息。

get("/x").then().assertThat().cookie("cookieName", "cookieValue"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", "cookieValue2"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", containsString("Value2")). ..

状态码

get("/x").then().assertThat().statusCode(200). ..
get("/x").then().assertThat().statusLine("something"). ..
get("/x").then().assertThat().statusLine(containsString("some")). ..
get("/x").then().assertThat().header("headerName", "headerValue"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", "headerValue2"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", containsString("Value2")). ..

还可以在验证头时使用映射函数。 例如,假设您要验证“Content-Length”头部小于1000.然后,您可以使用映射函数首先将头值转换为int,然后在使用Hamcrest验证前使用“整数” 匹配器:

get("/something").then().assertThat().header("Content-Length", Integer::parseInt, lessThan(1000));

Content-Type

get("/x").then().assertThat().contentType(ContentType.JSON). ..

内容全匹配

get("/x").then().assertThat().body(equalTo("something")). ..

关联类型验证

您可以使用响应中的数据来验证响应的另一部分。 例如,从服务端返回的以下JSON:

{ "userId" : "some-id", "href" : "http://localhost:8080/some-id" }

您可能会注意到,“href”属性以“userId”属性的值结尾。 如果我们想验证这个,我们可以实现一个io.restassured.matcher.ResponseAwareMatcher,可以:

get("/x").then().body("href", new ResponseAwareMatcher<Response>() {
                                  public Matcher<?> matcher(Response response) {
                                          return equalTo("http://localhost:8080/" + response.path("userId"));
                                  }
                       });

如果您使用Java 8,你可以使用lambda表达式:

get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId"));

有一些预定义的匹配器,您可以使用在io.restassured.matcher.RestAssuredMatchers(或io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers如果使用spring-mock-mvc模块)中定义。 例如:

get("/x").then().body("href", endsWithPath("userId"));

ResponseAwareMatchers也可以与另一个ResponseAwareMatcher或与Hamcrest Matcher组成。 例如:

get("/x").then().body("href", and(startsWith("http:/localhost:8080/"), endsWithPath("userId")));

and 方法是由io.restassured.matcher.ResponseAwareMatcherComposer静态导入的。

计算响应时间

从 REST Assured 2.8.0开始支持测量响应时间,例如:

long timeInMs = get("/lotto").time()

或使用特定时间单位:

long timeInSeconds = get("/lotto").timeIn(SECONDS);

其中SECONDS只是一个标准的TimeUnit。 您还可以使用DSL验证:

when().
      get("/lotto").
then().
      time(lessThan(2000L)); // Milliseconds

when().
      get("/lotto").
then().
      time(lessThan(2L), SECONDS);

需要注意的是,您只能参考性地将这些测量数据与服务器请求处理时间相关联(因为响应时间将包括HTTP往返和REST Assured处理时间等,不能做到十分准确)。

认证

REST assured还支持多种认证方案,例如OAuth,摘要,证书,表单和抢占式基本认证。 您可以为每个请求设置身份验证:

given().auth().basic("username", "password"). ..

也可以为所有请求定义身份验证:

RestAssured.authentication = basic("username", "password");

或者您也可以使用 specification.

基本认证

有两种类型的基本认证,抢占和“受质询的基本认证”。

抢占式

服务器在某些情况下给出未授权响应之前发送基本认证凭证,从而减少进行附加连接的开销。 大多数情况下可以这么使用:

given().auth().preemptive().basic("username", "password").when().get("/secured/hello").then().statusCode(200);

受质询的基本认证

使用“受质询的基本认证”时,REST Assured将不提供凭据,除非服务器已明确要求。 这意味着REST Assured将向服务器发出一个附加请求,以便进行质询,然后再次处理相同的请求,但此时会在header中设置基本凭据。

given().auth().basic("username", "password").when().get("/secured/hello").then().statusCode(200);

摘要认证

目前只支持受质询的摘要认证:

given().auth().digest("username", "password").when().get("/secured"). ..

表单认证

表单认证在互联网上非常流行。 它通常与用户在网页上填写其凭据(用户名和密码),然后在按某种类型的登录按钮时发起请求。 提供表单身份验证基础的一个非常简单的HTML页面可能如下所示

<html>
  <head>
    <title>Login</title>
  </head>

  <body>
    <form action="j_spring_security_check" method="POST">
      <table>
        <tr><td>User:&nbsp;</td><td><input type='text' name='j_username'></td></tr>
        <tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
          <tr><td colspan='2'><input name="submit" type="submit"/></td></tr>
       </table>
        </form>
      </body>
 </html>

也就是说 服务器期望用户填写“j_username”和“j_password”输入字段,然后按“提交”登录。 使用REST Assured,您可以测试受表单身份验证保护的服务,如下所示:

given().
        auth().form("John", "Doe").
when().
        get("/formAuth");
then().
        statusCode(200);

在REST中使用此类表单身份验证时,会导致为检索包含登录详细信息的网页而向服务器发出附加请求。 REST Assured将尝试解析此页面并查找两个输入字段(用户名和密码)以及表单操作的URI。 这可能失败,取决于网页的复杂性。 更好的选择是在设置表单身份验证时提供这些详细信息。 在这种情况下,可以:

given().
        auth().form("John", "Doe", new FormAuthConfig("/j_spring_security_check", "j_username", "j_password")).
when().
        get("/formAuth");
then().
        statusCode(200);

这样REST Assured不需要提出额外的请求并解析网页。 还有一个预定义的FormAuthConfig称为springSecurity,如果你使用默认的Spring Security属性,可以使用它:

given().
        auth().form("John", "Doe", FormAuthConfig.springSecurity()).
when().
        get("/formAuth");
then().
        statusCode(200);

CSRF

如今,服务器要求请求中提供一个CSRF token是常有的事了,这可以抵御多种类型的攻击。rest-assured支持解析并自动给服务器供应一个CSRF token。为此,rest-assured必须先发起一个追加请求来解析该网站(的部分内容)。

你可以通过下面的代码启用对CSRF的支持:

given().
        auth().form("John", "Doe", formAuthConfig().withAutoDetectionOfCsrf()).
when().
        get("/formAuth");
then().
        statusCode(200);

现在rest-assured将会自动尝试侦测这个网站是否包含CSRF token机制。为了使rest-assured的暴力破解更加顺利,可能会提供一个CSRF域的名称(这里我们假设我们正在使用Spring的安全默认值,因此我们可以使用预定义的springSecurity表单认证配置):

given().
        auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf")).
when().
        get("/formAuth");
then().
        statusCode(200);

我们至此已经告诉rest-assured去查找名为”_csrf”的CSRF域了(然而这虽然会比自动侦测更快,也更容易出错)。

默认情况下CSRF值将会作为一个请求参数,但是如果必要你也可以配置其放在请求的header中:

given().
        auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf").sendCsrfTokenAsHeader()).
when().
        get("/formAuth");
then().
        statusCode(200);

OAuth

为了使用OAuth1和OAuth2(关于查询/请求参数签名方面的机制),您需要添加Scribe到classpath中(如果你正在使用2.1.0或者更早之前版本的rest-assured,请参考旧版指南)。如果是maven请添加以下的依赖:

<dependency>
            <groupId>org.scribe</groupId>
            <artifactId>scribe</artifactId>
            <version>1.3.7</version>
            <scope>test</scope>
</dependency>

如果您没有使用maven,可以下载一个Scribe发行包并把它发在classpath下。

OAuth 1

OAuth1要求Scribe在classpath中。为使用auth1的认证您可以:

given().auth().oauth(..). ..

OAuth 2

自从2.5.0版本您可以依赖于Scribe使用OAuth2的认证:

given().auth().oauth2(accessToken). ..

这将会把OAuth2的accessToken放入header中。想要更加显式的操作可以:

given().auth().preemptive().oauth2(accessToken). ..

这里之所以存在given().auth().oauth2(..)这种语法是为了向后兼容(做的是相同的事情)。如果你需要在请求参数中提供一个OAuth2 token,您需要把Scribe放在classpath下,接下来:

given().auth().oauth2(accessToken, OAuthSignature.QUERY_STRING). ..

自定义身份验证

rest-assured允许您创建一个自定义的身份验证。你可以通过实现io.restassured.spi.AuthFilter接口,并作为一个过滤器。假设您的安全机制,是由两个header值相加然后组成一个新的叫做”AUTH”的header(当然这并不安全)。然后您可以这样做(Java 8的语法):

given().
        filter((requestSpec, responseSpec, ctx) -> {
            String header1 = requestSpec.getHeaders().getValue("header1");
            String header2 = requestSpec.getHeaders().getValue("header2");
            requestSpec.header("AUTH", header1 + header2);
            return ctx.next(requestSpec, responseSpec);
        }).
when().
        get("/customAuth").
then().
  statusCode(200);

使用AuthFilter而不是Filter的原因是,当我们执行given().auth().none(). ..类似这样的操作时AuthFilters会被自动移除。

Multi-part 表单数据

通常我们在向服务器传输大容量的数据时(译者注:比如文件)会使用multipart表单数据技术。rest-assured提供了一种multiPart方法来辨别这究竟是文件、二进制序列、输入流还是上传的文本。表单中上传一个文件可以这样:

given().
        multiPart(new File("/path/to/file")).
when().
        post("/upload");

它将会假设有一个control叫做”file”。在HTML中这意味着input标签的属性值为file。为了解释得更清楚请看下面的HTML表单:

<form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file" size="40">
        <input type=submit value="Upload!">
</form>

在这个例子中control的名字就是一个属性名为file的input标签。如果您使用的control名不是这个,需要指定:

given().
        multiPart("controlName", new File("/path/to/file")).
when().
        post("/upload");

在同一个请求中提供多个”multi-parts”事务也是可能的:

byte[] someData = ..
given().
        multiPart("controlName1", new File("/path/to/file")).
        multiPart("controlName2", "my_file_name.txt", someData).
        multiPart("controlName3", someJavaObject, "application/json").
when().
        post("/upload");

更多高级使用方法可以使用MultiPartSpecBuilder。举个例子:

Greeting greeting = new Greeting();
greeting.setFirstName("John");
greeting.setLastName("Doe");

given().
        multiPart(new MultiPartSpecBuilder(greeting, ObjectMapperType.JACKSON_2)
                .fileName("greeting.json")
                .controlName("text")
                .mimeType("application/vnd.custom+json").build()).
when().
        post("/multipart/json").
then().
        statusCode(200);

你可以通过使用MultiPartConfig指定默认的control名和文件名。举个例子:

given().config(config().multiPartConfig(multiPartConfig().defaultControlName("something-else"))). ..

这就会默认把control名配置为”something-else”而不是”file”。

其它用法请查阅 这篇博客。

对象映射

rest-assured支持从JSON和XML中映射Java对象。映射JSON需要classpath中有Jackson或者Gson才能使用,XML则需要JAXB。

序列化

假设我们有下面的Java对象:

public class Message {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

您需要将这个对象序列化为JSON并发送到请求中。可以有多种方式:

基于Content-Type的序列化

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json").
       body(message).
when().
      post("/message");

在这个例子里,由于请求中的content-type被设置为”application/json”,rest-assured也就会把对象序列化为JSON。rest-assured首先会在您的classpath中寻找Jackson,如果没有则使用Gson。如果您把请求中的content-type修改为”application/xml”,rest-assured将会使用JAXB把对象序列化为XML。如果没有指定content-type,rest-assured会按照以下的优先级进行序列化:

  1. 使用Jackson 2将对象序列化为JSON(Faster Jackson (databind))
  2. 使用Jackson将对象序列化为JSON(databind)
  3. 使用Gson将对象序列化为JSON
  4. 使用JAXB将对象序列化为XML

rest-assured也关心content-type的字符集(charset)等等。

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json; charset=UTF-16").
       body(message).
when().
      post("/message");

您也可以把Message这个实例序列化为一个表单参数:

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json; charset=UTF-16").
       formParam("param1", message).
when().
      post("/message");

这个message对象将会被实例化为utf-16编码的JSON(如果有Jackson或者Gson)。

由HashMap创建JSON

您也可以提供一个Map,由此rest-assured可以创建一个JSON。

Map<String, Object>  jsonAsMap = new HashMap<>();
jsonAsMap.put("firstName", "John");
jsonAsMap.put("lastName", "Doe");

given().
        contentType(JSON).
        body(jsonAsMap).
when().
        post("/somewhere").
then().
        statusCode(200);

这将会产生一个JSON数据(JSON payload):

{ "firstName" : "John", "lastName" : "Doe" }

使用显式序列化器

如果您的classpath中同时有多个对象、或者不考虑content-type的设置,可以显示地指定一个序列化器。

Message message = new Message();
message.setMessage("My messagee");
given().
       body(message, ObjectMapperType.JAXB).
when().
      post("/message");

在这个例子中message对象将会被JAXB序列化为一个XML。

反序列化

让我们再次假设我们有以下的Java对象:

public class Message {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

我们需要把响应体反序列化为一个Message对象。

基于Content-Type的反序列化

假设服务端返回一个这样的JSON:

{"message":"My message"}

将它反序列化为一个Message对象:

Message message = get("/message").as(Message.class);

为此响应体的content-type必须是”application/json”(或者其它包含“json”的类型)。如果服务端返回:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
      <message>My message</message>
</message>

且content-type是”application/xml”,代码可以完全不用修改:

Message message = get("/message").as(Message.class);

自定义类型content-type反序列化

如果服务端返回一个自定义的content-type,假设是”application/something”,你仍然想使用rest-assured的对象映射的话,这有两种方法。你可以使用显式指定的方法或者为自定义的content-type注册一个解析器:

Message message = expect().parser("application/something", Parser.XML).when().get("/message").as(Message.class);

Message message = expect().defaultParser(Parser.XML).when().get("/message").as(Message.class);

你也可以注册一个默认解析器,或者静态式注册一个自定义的解析器,也可以使用模式(specifications)。

使用显式反序列化器

如果您的classpath下同时有多个对象或者不在意响应体的content-type,你可以使用显示的反序列化器。

Message message = get("/message").as(Message.class, ObjectMapperType.GSON);

配置

您可以使用ObjectMapperConfig配置预定义的对象映射,并传递给细节配置。举个例子,你可以将GSON的命名策略改为LowerCaseWithUnderscores(译者注:一种策略,将大写字母改为小写字母并添加下划线):

RestAssured.config = RestAssuredConfig.config().objectMapperConfig(objectMapperConfig().gsonObjectMapperFactory(
                new GsonObjectMapperFactory() {
                    public Gson create(Class cls, String charset) {
                        return new GsonBuilder().setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
                    }
                }
        ));

这里为GSON、JAXB、Jackson和Faster Jackson都预定义了用来映射实例的工厂。

自定义

默认情况下rest-assured将会扫描classpath中各种各样的对象映射。如果您想要集成一种对象映射,默认是不支持的,如果您已经做好了封装,可以实现io.restassured.mapper.ObjectMapper 接口。告诉rest-assured使用您的对象映射或者将其作为body方法里的第二个参数:

given().body(myJavaObject, myObjectMapper).when().post("..")

或者您可以静态式定义:

RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig(myObjectMapper));

更多例子参阅这里。

自定义解析器

rest-assured提供了预定义的解析器,例如HTML、XML和JSON的。但是您可以通过注册预置的解析器来解析现在不支持的内容类型:

RestAssured.registerParser(<content-type>, <parser>);

例如注册一个可以解析’application/vnd.uoml+xml’类型的XML解析器:

RestAssured.registerParser("application/vnd.uoml+xml", Parser.XML);

您也可以注销一个解析器:

RestAssured.unregisterParser("application/vnd.uoml+xml");

解析器可以指定于每个请求中:

get(..).then().using().parser("application/vnd.uoml+xml", Parser.XML). ..;

然后使用模式。

默认解析器

有时如果响应中不包含任何content-type,指定一个默认的解析器会很有用。

RestAssured.defaultParser = Parser.JSON;

你也可以为一次请求指定默认的解析器:

get("/x").then().using().defaultParser(Parser.JSON). ..

或者使用响应体模式。

默认值

rest-assured发起请求时默认使用localhost的8080端口.如果你需要换个端口:

given().port(80). ..

或者简单些:

..when().get("http://myhost.org:80/doSomething");

您也可以改变默认的基本URI、基本路径、端口和认证scheme:

RestAssured.baseURI = "http://myhost.org";
RestAssured.port = 80;
RestAssured.basePath = "/resource";
RestAssured.authentication = basic("username", "password");
RestAssured.rootPath = "x.y.z";

这意味着类似get("/hello")这样的请求实际上会被解析为http://myhost.org:80/resource/hellohttp://code.google.com/p/rest-assured/wiki/Usage#Root_path)。其它的默认值也可以被指定:,附带身份认证中的用户名和密码属性。关于设置根路径参考[这里](

RestAssured.filters(..); // List of default filters
RestAssured.requestSpecification = .. // Default request specification
RestAssured.responseSpecification = .. // Default response specification
RestAssured.urlEncodingEnabled = .. // Specify if Rest Assured should URL encoding the parameters
RestAssured.defaultParser = .. // Specify a default parser for response bodies if no registered parser can handle data of the response content-type
RestAssured.registerParser(..) // Specify a parser for the given content-type
RestAssured.unregisterParser(..) // Unregister a parser for the given content-type

您可以设置重置为标准的baseURI (localhost),basePath(空),标准端口(8080),标准根路径(“”),默认身份认证scheme(none)和URL编码启用(true):

RestAssured.reset();

模式复用

与其复制一份响应的断言或者请求参数(的代码)到另一个测试用例中,我们可以使用 RequestSpecBuilder或者ResponseSpecBuilder定义一个规范提案。

举个例子,在多个测试用例中,我们都涉及到这样的内容:判断响应码是否为200,JSON数组x.y的长度是否是2,您可以定义一个ResponseSpecBuilder:

ResponseSpecBuilder builder = new ResponseSpecBuilder();
builder.expectStatusCode(200);
builder.expectBody("x.y.size()", is(2));
ResponseSpecification responseSpec = builder.build();

// Now you can re-use the "responseSpec" in many different tests:
when().
       get("/something").
then().
       spec(responseSpec).
       body("x.y.z", equalTo("something"));

在这个例子中需要重用的数据定义并合并在”responseSpec”,并且仅当所有预期都通过时用例才能通过。

您也可以将相同的请求参数重用:

RequestSpecBuilder builder = new RequestSpecBuilder();
builder.addParam("parameter1", "parameterValue");
builder.addHeader("header1", "headerValue");
RequestSpecification requestSpec = builder.build();

given().
        spec(requestSpec).
        param("parameter2", "paramValue").
when().
        get("/something").
then().
        body("x.y.z", equalTo("something"));        

这里请求数据合并在”requestSpec”中,由此上面例子中实际请求参数包括两个(“parameter1” 和 “parameter2”)和一个header(“header1”)。

过滤器

过滤器会在请求实际发起之前侦测和改变该请求的内容,也可以在响应体实际返回之前拦截并改变。您可以将其理解为AOP中的around advice(译者注:可以自行搜索切片编程)。过滤器也可以用在认证scheme、session管理、日志中。创建一个过滤器需要实现io.restassured.filter.Filter接口。使用过滤器:

given().filter(new MyFilter()). ..

rest-assured提供了几个过滤器:

  1. io.restassured.filter.log.RequestLoggingFilter: 可以打印出请求模式的细节。
  2. io.restassured.filter.log.ResponseLoggingFilter: 可以打印响应信息的细节如果响应体的状态码匹配given方法的参数。
  3. io.restassured.filter.log.ErrorLoggingFilter: 如果发生了异常(状态码在400和500之间),过滤器将会打印响应的内容。

Response Builder

如果您想要通过一个过滤器改变Response ,可以使用ResponseBuilder创建一个基于原始response的新实例。比如把原始响应体改为something:

Response newResponse = new ResponseBuilder().clone(originalResponse).setBody("Something").build();

日志

在大量的用例中,打印出响应或者请求的细节将有助于创建正确的预期、发送准确的请求。为此您可以使用rest-assured预定义的过滤器,或者使用其中的快捷方法。

请求日志

自1.5版本起rest-assured支持对特定的请求打日志,之前的做法是使用RequestLoggingFilter(译者注:应该是通过过滤器进行控制)。注意打印日志后HTTP Builder和HTTP Client会添加额外的header内容。这个过滤器可以只记录特定请求的特定细节。换句话说,你可以不关注RequestLoggingFilter记录的有关实际往服务端发送的内容。因为随后的其它过滤器会在日志记录后改变这个请求。如果你想要记录实际发送的内容,参阅HTTP Client logging docs or use an external tool such Wireshark,或者使用第三方工具例如Wireshark。示例如下:

given().log().all(). .. // Log all request specification details including parameters, headers and body
given().log().params(). .. // Log only the parameters of the request
given().log().body(). .. // Log only the request body
given().log().headers(). .. // Log only the request headers
given().log().cookies(). .. // Log only the request cookies
given().log().method(). .. // Log only the request method
given().log().path(). .. // Log only the request path

响应日志

如果您想打印除了状态码以外的响应信息,可以:

get("/x").then().log().body() ..

这样做,无论是否有异常错误发生,都会打印出响应信息。如果您希望只有当错误发生时才打印响应信息,可以:

get("/x").then().log().ifError(). .. 

您也可以记录响应里包括状态码、header、cookie的所有细节:

get("/x").then().log().all(). .. 

也可以只记录状态码、header或者cookie:

get("/x").then().log().statusLine(). .. // Only log the status line
get("/x").then().log().headers(). .. // Only log the response headers
get("/x").then().log().cookies(). .. // Only log the response cookies

您也可以配置为仅当状态码匹配某个值时才打印响应体:

get("/x").then().log().ifStatusCodeIsEqualTo(302). .. // Only log if the status code is equal to 302
get("/x").then().log().ifStatusCodeMatches(matcher). .. // Only log if the status code matches the supplied Hamcrest matcher

认证失败日志

自rest-assured2.3.1版本起,您可以仅当认证失败时记录请求或者响应的日志。为请求打日志:

given().log().ifValidationFails(). ..

为响应打日志:

.. .then().log().ifValidationFails(). ..

同时启用对请求和响应的认证失败记录,可以使用LogConfig:

given().config(RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails(HEADERS))). ..

认证失败,仅记录header。

还有种针对所有请求的简单写法:

RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();

根路径

为避免在body方法里使用重复的路径,您可以指定一个根路径:

when().
         get("/something").
then().
         body("x.y.firstName", is(..)).
         body("x.y.lastName", is(..)).
         body("x.y.age", is(..)).
         body("x.y.gender", is(..));

使用根路径:

when().
        get("/something").
then().
         root("x.y"). // You can also use the "root" method
         body("firstName", is(..)).
         body("lastName", is(..)).
         body("age", is(..)).
         body("gender", is(..));

也可以设置一个默认的根路径:

RestAssured.rootPath = "x.y";

在许多高级用例中,在根路径上附加一些参数也很有用。您可以使用appendRoot方法:

when().
         get("/jsonStore").
then().
         root("store.%s", withArgs("book")).
         body("category.size()", equalTo(4)).
         appendRoot("%s.%s", withArgs("author", "size()")).
         body(withNoArgs(), equalTo(4));

也可以对根路径进行拆分:

when().
         get("/jsonStore").
then().
         root("store.category").
         body("size()", equalTo(4)).
         detachRoot("category").
         body("size()", equalTo(1));

路径参数

在预定义的路径包含变量时,路径参数会很有用。举个例子:

String someSubPath = "else";
int index = 1;
get("/x").then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..

将会对”something.else[0](译者注:这里只是举个例子)“是否等于”some value”进行判断。

另一种用法是,如果您有复杂的根路径:

when().
       get("/x").
then().
       root("filters.filterConfig[%d].filterConfigGroups.find { it.name == 'GroupName' }.includes").
       body(withArgs(0), hasItem("first")).
       body(withArgs(1), hasItem("second")).
       ..

路径参数遵循Java的标准格式语法。

注意withArgs方法可以从io.restassured.RestAssured类中静态导入。

有时当所有在根路径中指定的参数都已经验证过了,只想要验证一个不含多余参数的body时,可以使用withNoArgs

when().
         get("/jsonStore").
then().
         root("store.%s", withArgs("book")).
         body("category.size()", equalTo(4)).
         appendRoot("%s.%s", withArgs("author", "size()")).
         body(withNoArgs(), equalTo(4));

Session支持

rest-assured提供了一套简单的管理session的方式。您可以在DSL(译者注:领域特定语言)中预定义一个session的id值:

given().sessionId("1234"). .. 

上面的代码实际上是这个的简写:

given().cookie("JSESSIONID", "1234"). .. 

您也可以为所有的请求指定一个默认的sessionId

RestAssured.sessionId = "1234";

默认情况下session id的名字是JSESSIONID,但是您可以通过使用SessionConfig来修改:

RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));

您也可以指定一个sessionid并在其它用例中复用,使用RequestSpecBuilder

RequestSpecBuilder spec = new RequestSpecBuilder().setSessionId("value1").build();

// Make the first request with session id equal to value1
given().spec(spec). .. 
// Make the second request with session id equal to value1
given().spec(spec). .. 

从响应对象中获取一个session id:

String sessionId = get("/something").sessionId();

Session过滤器

2.0.0版本起您可以使用session filter截获并提供一个session,举个例子:

SessionFilter sessionFilter = new SessionFilter();

given().
          auth().form("John", "Doe").
          filter(sessionFilter).
when().
          get("/formAuth").
then().
          statusCode(200);


given().
          filter(sessionFilter). // Reuse the same session filter instance to automatically apply the session id from the previous response
when().
          get("/x").
then().
          statusCode(200);

要想获取由SessionFilter截获的session id:

String sessionId = sessionFilter.getSessionId();

SSL

在多数场景下,SSL能顺利运转,这多亏于HTTP Builder和HTTP Client。如果服务端使用了无效的证书,然而有些例子下还是会出错。最简单的方法您可以使用”relaxed HTTPs validation”:

given().relaxedHTTPSValidation().when().get("https://some_server.com"). .. 

也可以为所有的请求静态定义这个配置:

RestAssured.useRelaxedHTTPSValidation();

或者放在请求指定中进行复用。

这将会假定我们使用SSL协议。想要改变需要覆盖relaxedHTTPSValidation方法:

given().relaxedHTTPSValidation("TLS").when().get("https://some_server.com"). .. 

您也可以创建一个Java keystore文件并执行一些细粒度的操作。这并不难,首先阅读指南,然后在rest-assured中使用keystore:

given().keystore("/pathToJksInClassPath", <password>). .. 

或者为每一个请求指定:

RestAssured.keystore("/pathToJksInClassPath", <password>);

您也可以定义一个可复用的keystore。

如果您已经使用密码载入了一个keystore,可以将其作为可信的keystore:

RestAssured.trustStore(keystore);

在这也可以找到一些相关的示例。

有关更多SSL的高级配置参阅SSL Configuration。

SSL 无效主机名

如果证书指定了一个无效的主机名,并且您无需创建和导入一个keystore。自2.2.0起您可以:

RestAssured.config = RestAssured.config().sslConfig(sslConfig().allowAllHostnames());

让所有请求支持所有的主机名。

given().config(RestAssured.config().sslConfig(sslConfig().allowAllHostnames()). .. ;

对于单个请求。

注意如果您使用了”relaxed HTTPs validation”,那么allowAllHostnames将会默认启用。

URL 编码

由于rest-assured提供了优秀的自动编码,通常您无需考虑URL编码的事情。在有些用例中仍然需要关闭URL编码的功能。其中一个原因是在使用rest-assured之前可能你已经做过一次编码。为防止两次URL编码您需要告诉rest-assured禁用这个功能。

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();
..

或者

RestAssured.baseURI = "https://jira.atlassian.com";
RestAssured.port = 443;
RestAssured.urlEncodingEnabled = false;
final String query = "project%20=%20BAM%20AND%20issuetype%20=%20Bug";
String response = get("/rest/api/2.0.alpha1/search?jql={q}", query);
..

代理(proxy)配置

从版本2.3.2开始REST Assured可以更好地支持代理。 例如,如果你有一个代理在localhost端口8888你可以做:

given().proxy("localhost", 8888). .. 

如果服务器在本地环境中运行,可以不指定主机名:

given().proxy(8888). .. // Will assume localhost

要使用HTTPS,需要提供第三个参数(scheme)或使用io.restassured.specification.ProxySpecification。 例如:

given().proxy(host("localhost").withScheme("https")). ..

其中host从io.restassured.specification.ProxySpecification静态导入。

从版本2.7.0开始,您还可以为代理指定抢占式基本身份验证。 例如:

given().proxy(auth("username", "password")).when() ..

其中auth是从io.restassured.specification.ProxySpecification静态导入的,也可以将身份验证与不同的host相结合:

given().proxy(host("http://myhost.org").withAuth("username", "password")). ..

静态代理配置

可以为所有请求配置静态代理,例如:

RestAssured.proxy("localhost", 8888);    

或者:

RestAssured.proxy = host("localhost").withPort(8888);

请求规范代理配置

您还可以创建请求规范并在其中指定代理:

RequestSpecification specification = new RequestSpecBuilder().setProxy("localhost").build();
given().spec(specification). ..

详细配置

详细配置由RestAssuredConfig实例提供,您可以使用它配置HTTP Client的参数以及重定向,日志,编码器,解码器,Session,ObjectMapper,Connection,SSL和ParamConfig 设置。 例子:

特定请求:

given().config(RestAssured.config().redirect(redirectConfig().followRedirects(false))). ..

或使用RequestSpecBuilder:

RequestSpecification spec = new RequestSpecBuilder().setConfig(RestAssured.config().redirect(redirectConfig().followRedirects(false))).build();

或所有请求:

RestAssured.config = config().redirect(redirectConfig().followRedirects(true).and().maxRedirects(0));

config() and newConfig() can be statically imported from io.restassured.config.RestAssuredConfig.

编码配置

使用[EncoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/EncoderConfig.htmlencoding),可以指定默认的内容 charset(如果没有在content-type头中指定)和查询参数charset为所有请求。 如果未指定内容字符集,则使用ISO-8859-1,如果未指定查询参数字符集,则使用UTF-8。 用法示例:

RestAssured.config = RestAssured.config().encoderConfig(encoderConfig().defaultContentCharset("US-ASCII"));

如果没有通过使用EncoderConfig中的defaultCharsetForContentType方法为此内容类型显式定义字符集,那么还可以指定用于特定内容类型的编码器字符集。 例如:

RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));

这将假设明确指定一个字符集的“application / xml”内容类型的UTF-16编码。 默认情况下,“application / json”被指定为使用“UTF-8”作为默认内容类型,这是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。

避免自动将字符集添加到content-type标头

默认情况下,REST Assured会自动添加字符集标头。 要完全禁用这个,你可以这样配置EncoderConfig

RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false));

解码配置

使用[DecoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/DecoderConfig.html),您可以将默认响应内容解码字符集设置为 所有响应。 如果您期望使用不同于ISO-8859-1的内容字符集(这是默认字符集),并且响应未在内容类型标头中定义字符集,那么这将非常有用。 用法示例:

RestAssured.config = RestAssured.config().decoderConfig(decoderConfig().defaultContentCharset("UTF-8"));

您还可以使用“DecoderConfig”来指定要应用哪些内容解码器。 当你这样做时,“Accept-Encoding”头部将被自动添加到请求中,并且响应主体将被自动解码。 默认情况下,启用GZIP和DEFLATE解码器。 例如要删除GZIP解码但保留DEFLATE解码,您可以执行以下操作:
您还可以使用“DecoderConfig”来指定要应用哪些内容解码器。 当你这样做时,“Accept-Encoding”头部将被自动添加到请求中,并且响应主体将被自动解码。 默认情况下,启用GZIP和DEFLATE解码器。 例如要删除GZIP解码但保留DEFLATE解码,您可以执行以下操作:

given().config(RestAssured.config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))). ..

如果没有通过使用DecoderConfig中的“defaultCharsetForContentType”方法为此内容类型明确定义字符集,则还可以指定要用于特定内容类型的解码器字符集。 例如:

RestAssured.config = config(config().decoderConfig(decoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));

这将假设明确指定一个字符集的“application / xml”内容类型的UTF-16编码。 默认情况下,“application / json”使用“UTF-8”作为默认字符集,这是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。

Session配置

使用Session配置,您可以配置REST Assured使用的默认session ID名称。 默认session id名称是JSESSIONID,如果应用程序中的名称不同,并且您希望使用REST Assured的[会话支持](#Session_support),您只需更改它。 用法:

RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));

重定向 DSL

重定向配置可以使用DSL指定。 例如。

given().redirects().max(12).and().redirects().follow(true).when(). .. 

网络连接配置

让您配置REST Assured的连接设置。 例如,如果要在每个响应后强制关闭Apache HTTP Client连接。 如果您在响应中使用少量数据进行大量快速连续请求,则可能需要执行此操作。然而,如果你正在下载(尤其是大量的)分块,你绝不能每个响应后关闭连接的数据。 默认情况下,连接在每个响应后不关闭。

RestAssured.config = RestAssured.config().connectionConfig(connectionConfig().closeIdleConnectionsAfterEachResponse());

Json Config

JsonPathConfig允许您在REST Assured或 JsonPath使用时配置Json设置。 它让你配置如何处理JSON数字。

RestAssured.config = RestAssured.config().jsonConfig(jsonConfig().numberReturnType(NumberReturnType.BIG_DECIMAL))

HTTP Client 配置

为REST Assured将在执行请求时使用的HTTP Client实例配置属性。 默认情况下,REST Assured会为每个“given”语句创建一个新的http Client实例。 要配置重用,请执行以下操作:

RestAssured.config = RestAssured.config().httpClient(httpClientConfig().reuseHttpClientInstance());

您还可以使用httpClientFactory方法提供自定义HTTP Client实例,例如:

RestAssured.config = RestAssured.config().httpClient(httpClientConfig().httpClientFactory(
         new HttpClientConfig.HttpClientFactory() {

            @Override
            public HttpClient createHttpClient() {
                return new SystemDefaultHttpClient();
            }
        }));

注意,目前你需要提供一个AbstractHttpClient的实例.

也可以配置默认参数等。

SSL 配置

SSLConfig 允许您指定更高级的SSL配置,如信任库,密钥库类型和主机名验证器。 例如:

RestAssured.config = RestAssured.config().sslConfig(sslConfig().with().keystoreType(<type>).and().strictHostnames());

参数配置

ParamConfig 允许您配置在“冲突”时,更新不同的参数。 默认情况下,所有参数都将合并,因此如果您执行以下操作:

given().queryParam("param1", "value1").queryParam("param1", "value2").when().get("/x"). ...

REST Assured将发送一个查询字符串param1 = value1&param1 = value2
如果这不是您想要的,你可以配置REST Assured为* replace *值代替:

given().
        config(config().paramConfig(paramConfig().queryParamsUpdateStrategy(REPLACE))).
        queryParam("param1", "value1").
        queryParam("param1", "value2").
when().
        get("/x"). ..

REST Assured现在将替换param1的值为value2(因为它是最后写的),而不是将它们合并在一起。 您也可以为所有参数类型的每种类型配置统一的更新策略

given().config(config().paramConfig(paramConfig().replaceAllParameters())). ..

也支持在[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)。

Spring Mock Mvc 模块

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控制器:

@Controller
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping(value = "/greeting", method = GET)
    public @ResponseBody Greeting greeting(
            @RequestParam(value="name", required=false, defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}

你可以使用[RestAssuredMockMvc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)来测试它,像这样:

given().
        standaloneSetup(new GreetingController()).
        param("name", "Johan").
when().
        get("/greeting").
then().
        statusCode(200).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));  

即它与标准的REST Assured语法非常相似。 这使得运行测试真的很快,并且比标准的REST Assured更容易引导环境和使用mock。 你常用的标准REST Assured中的大多数东西都可以使用RestAssured Mock Mvc。 例如(某些)配置,静态规范,日志等等。要使用它,你需要依赖于Spring Mock Mvc模块:

<dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>spring-mock-mvc</artifactId>
      <version>3.0.1</version>
      <scope>test</scope>
</dependency>

或者[下载](http://dl.bintray.com/johanhaleby/generic/spring-mock-mvc-3.0.1-dist.zip)。

Bootstrapping RestAssuredMockMvc

静态导入方法:

io.restassured.module.mockmvc.RestAssuredMockMvc.*
io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers.*

有关其他静态导入,请参阅文档的[静态导入](#static-imports)部分。

为了使用RestAssuredMockMvc启动测试,您需要使用一组控制器,MockMvc实例或Spring的WebApplicationContext来初始化它。您可以对单个请求执行此操作,如上例所示:

given().standaloneSetup(new GreetingController()). ..

也可以使用静态方法:

RestAssuredMockMvc.standaloneSetup(new GreetingController());

如果静态定义,则不必在DSL中指定任何控制器(或MockMvc或WebApplicationContext实例)。这意味着前面的例子可以写成:

given().
        param("name", "Johan").
when().
        get("/greeting").
then().
        statusCode(200).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));  

异步请求

从版本2.5.0 RestAssuredMockMvc支持异步请求。例如,假设有以下控制器

@Controller
public class PostAsyncController {

    @RequestMapping(value = "/stringBody", method = POST)
    public @ResponseBody
    Callable<String> stringBody(final @RequestBody String body) {
        return new Callable<String>() {
            public String call() throws Exception {
                return body;
            }
        };
    }
}

你可以这样测试:

given().
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

默认超时为1秒。也可以使用DSL更改超时:

given().
        body("a string").
when().
        async().with().timeout(20, TimeUnit.SECONDS).post("/stringBody").
then().
        body(equalTo("a string"));    

还可以使用AsyncConfig),例如:

given().
        config(config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS))).
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

withTimeout是从io.restassured.module.mockmvc.config.AsyncConfig静态导入的,只是创建一个具有给定超时的AsyncConfig的快捷方式。全局应用配置以应用于所有请求:

RestAssuredMockMvc.config = RestAssuredMockMvc.config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS));

// Request 1
given().
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

// Request 2
given().
        body("another string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

请求1和2现在将使用默认超时100毫秒。

添加请求后处理器

Spring MockMvc已经对请求后处理器做了支持,并且您也可以在RestAssuredMockMvc中使用。举个例子:

given().postProcessors(myPostProcessor1, myPostProcessor2). ..

请注意,推荐从org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors(例如认证相关的 RequestPostProcessors)加入请求后处理器时,直接使用auth方法,这样可读性更强,当然结果是一样的:

given().auth().with(httpBasic("username", "password")). ..

这里的httpBasic静态导入自SecurityMockMvcRequestPostProcessor。

添加结果处理器

Spring MockMvc对结果处理器 做了支持,您也可以在RestAssuredMockMvc中使用。比方说您想要使用原生的MockMvc日志功能:

.. .then().apply(print()). .. 

这里的print静态导入自org.springframework.test.web.server.result.MockMvcResultHandlers。请注意如果您正在使用2.6.0或更早版本的rest-assured,需要这样使用结果处理器:

given().resultHandlers(print()). .. 

但是rest-assured2.8.0起不推荐使用这种语法。

使用结果匹配器

Spring MockMvc提供了许多结果处理器,您可以从中获益。RestAssuredMockMvc也对其中必要的功能进行支持。举个例子,基于某种原因您想要使用结果匹配器验证状态码是否等于200:

given().
        param("name", "Johan").
when().
        get("/greeting").
then().
        assertThat(status().isOk()).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));  

这里的status静态导入自org.springframework.test.web.server.result.MockMvcResultMatchers。注意,您可以使用expect方法,功能上和assertThat一样,但是更接近原生的MockMvc的语法。

拦截器

您也可以在请求(译者注:这里指mock请求)曝光之前截住并改变MockHttpServletRequestBuilder。您需要先定义一个MockHttpServletRequestBuilderInterceptor,并在RestAssuredMockMvc中使用:

given().interceptor(myInterceptor). ..

Specifications

正如标准的Res​​t Assured,你可以使用specifications,以便更好地重用。请注意,RestAssuredMockMvc的请求规范构建器称为MockMvcRequestSpecBuilder。同样的ResponseSpecBuilder 也可以在RestAssuredMockMvc中使用。规格可以静态定义,就像标准的Res​​t Assured一样。例如:

RestAssuredMockMvc.requestSpecification = new MockMvcRequestSpecBuilder().addQueryParam("name", "Johan").build();
RestAssuredMockMvc.responseSpecification = new ResponseSpecBuilder().expectStatusCode(200).expectBody("content", equalTo("Hello, Johan!")).build();

given().
        standaloneSetup(new GreetingController()).
when().
        get("/greeting").
then().
        body("id", equalTo(1));

重置 RestAssuredMockMvc

如果您使用了任何静态配置,您可以通过调用RestAssuredMockMvc.reset()方法轻松地将RestAssuredMockMvc重置为其默认状态。

Spring MVC认证

spring-mock-mvc的版本`2.3.0’支持身份验证。例如:

given().auth().principal(..). ..

一些认证方法需要Spring安全在类路径(可选)。也可以静态定义认证:

RestAssuredMockMvc.authentication = principal("username", "password");

其中principal方法是从RestAssuredMockMvc静态导入的。还可以在请求构建器中定义认证方案:

MockMvcRequestSpecification spec = new MockMvcRequestSpecBuilder.setAuth(principal("username", "password")).build();

使用 Spring Security 测试

从版本2.5.0也有更好的支持Spring Security。如果你在类路径中有spring-security-test,你可以这样做:

given().auth().with(httpBasic("username", "password")). ..

其中httpBasic是从SecurityMockMvcRequestPostProcessor静态导入的。这将对请求应用基本认证。为了这个工作,你需要应用SecurityMockMvcConfigurer到MockMvc实例。您可以手动执行此操作:

MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).apply(SecurityMockMvcConfigurers.springSecurity()).build();

or RESTAssuredMockMvc will automatically try to apply the springSecurity configurer automatically if you initalize it with an instance of AbstractMockMvcBuilder, for example when configuring a “web app context”:

given().webAppContextSetup(context).auth().with(httpBasic("username", "password")). ..

Here’s a full example:

import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;

import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfiguration.class)
@WebAppConfiguration
public class BasicAuthExample {

    @Autowired
    private WebApplicationContext context;

    @Before public void
    rest_assured_is_initialized_with_the_web_application_context_before_each_test() {
        RestAssuredMockMvc.webAppContextSetup(context);
    }

    @After public void
    rest_assured_is_reset_after_each_test() {
        RestAssuredMockMvc.reset();
    }

    @Test public void
    basic_auth_example() {
        given().
                auth().with(httpBasic("username", "password")).
        when().
                get("/secured/x").
        then().
                statusCode(200).
                expect(authenticated().withUsername("username"));
    }
}

You can also define authentication for all request, for example:

RestAssuredMockMvc.authentication = with(httpBasic("username", "password"));

where with is statically imported from io.restassured.module.mockmvc.RestAssuredMockMvc. It’s also possible to use a request specification.

注入一个用户

也可以使用Spring Security测试注释,例如@WithMockUser和 @WithUserDetails。例如,假设您想测试此控制器:

@Controller
public class UserAwareController {

    @RequestMapping(value = "/user-aware", method = GET)
    public
    @ResponseBody
    String userAware(@AuthenticationPrincipal User user) {
        if (user == null || !user.getUsername().equals("authorized_user")) {
            throw new IllegalArgumentException("Not authorized");
        }

        return "Success");
    }
}

您可以看到`userAware’方法需要一个 User 作为参数,我们让Spring Security使用@AuthenticationPrincipal 注入它。要生成测试用户,我们可以这样做:

@WithMockUser(username = "authorized_user")
@Test public void
spring_security_mock_annotations_example() {
    given().
            webAppContextSetup(context).
     when().
            get("/user-aware").
     then().
            statusCode(200).
            body(equalTo("Success")).
            expect(authenticated().withUsername("authorized_user"));
}

注意,也可以不使用注释,而是使用RequestPostProcessor ,例如SecurityMockMvcRequestPostProcessors#user(java.lang.String).

参数相关

MockMvc没有区分不同类型的参数,所以paramformParamqueryParam目前只是委托给MockMvc中的param。 formParam自动添加application / x-www-form-urlencoded内容类型的头部,就像标准的Res​​t Assured一样。

Scala支持

REST Assured 2.6.0引入了将“别名”添加到“then”方法的scala-support模块定义在Response或MockMvcResponse中调用“Then”。这样做的原因是then在将来可能是Scala中的保留关键字,并且当使用具有此名称的方法时,编译器会发出警告。要启用Then,只需从scala-support模块导入io.restassured.module.scala.RestAssuredSupport.AddThenToResponse类。例如:

import io.restassured.RestAssured.when
import io.restassured.module.scala.RestAssuredSupport.AddThenToResponse
import org.hamcrest.Matchers.equalTo
import org.junit.Test

@Test
def `trying out rest assured in scala with implicit conversion`() {
  when().
          get("/greetJSON").
  Then().
          statusCode(200).
          body("key", equalTo("value"))
}

注意:同样支持 Spring Mock Mvc Module.

可以像这样使用它:

SBT:

libraryDependencies += "io.rest-assured" % "scala-support" % "3.0.1"

Maven:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>scala-support</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>

Gradle:

testCompile 'io.rest-assured:scala-support:3.0.1'

No build manager:

手动下载 distribution file 。

Kotlin支持

Kotlin是由JetBrains开发的一种语言,它与Java和REST Assured非常好地集成。当使用它与REST Assured有一件事,必须逃避when,因为它是Kotlin中的保留关键字。例如:

Test fun kotlin_rest_assured_example() {
    given().
            param("firstName", "Johan").
            param("lastName", "Haleby").
    `when`().
            get("/greeting").
    then().
            statusCode(200).
            body("greeting.firstName", equalTo("Johan")).
            body("greeting.lastName", equalTo("Haleby"))
}

为了解决这个问题,创建一个extension function,创建一个别名为when时叫做When

fun RequestSpecification.When(): RequestSpecification {
    return this.`when`()
}

代码现在可以像这样写:

Test fun kotlin_rest_assured_example() {
    given().
            param("firstName", "Johan").
            param("lastName", "Haleby").
    When().
            get("/greeting").
    then().
            statusCode(200).
            body("greeting.firstName", equalTo("Johan")).
            body("greeting.lastName", equalTo("Haleby"))
}

注意,我们不需要任何转义。有关更多详细信息,请参阅this博客文章。

更多信息

其他相关信息,请参考 javadoc:

  • RestAssured
  • RestAssuredMockMvc Javadoc
  • Specification package

一些代码示例:

  • REST Assured tests
  • JsonPathTest
  • XmlPathTest

如果你需要支持,可以加入 mailing list.

如果需要专业支持,请联系 johanhaleby.

* 注:本文来自网络投稿,不代表本站立场,如若侵犯版权,请及时知会删除