验证响应数据
您还可以验证状态码,状态行,Cookie,headers,内容类型和正文。
响应体
您还可以将响应正文映射到Java对象,单击这里 了解详细信息。
Cookie
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")). ..
Header
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静态导入的。
@Test
public void testtwentynine(){
//关联型验证
//{ "userId" : "some-id", "href" : "http://localhost:8080/some-id" } ,验证“href”属性以“userId”属性的值结尾
given().proxy(8888).get("http://localhost:8889/getuserid").then()
.body("href", new ResponseAwareMatcher<Response>() {
@Override
public Matcher<?> matcher(Response response) throws Exception {
return equalTo("http://localhost:8080/"+response.path("userId"));
}
});
given()
.proxy(8888)
.when()
.get("http://localhost:8889/getuserid")
.then()
.body("href",response -> equalTo("http://localhost:8080/"+response.path("userId") ));
given()
.proxy(8888)
.when()
.get("http://localhost:8889/getuserid")
.then()
.body("href",endsWithPath("userId") );
given()
.proxy(8888)
.when()
.get("http://localhost:8889/getuserid")
.then()
.body("href", ResponseAwareMatcherComposer.and(startsWith("http://localhost:8080/"),endsWithPath("userId")) );
}
计算响应时间
从 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: </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会按照以下的优先级进行序列化:
使用Jackson 2将对象序列化为JSON(Faster Jackson (databind))
使用Jackson将对象序列化为JSON(databind)
使用Gson将对象序列化为JSON
使用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)。
实际代码:
实体类
package com.course.LearnRestassuredCase;
public class LoginDATA {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
测试方法:
@Test
public void testthirty(){
//对象映射成json 作为参数传参
LoginDATA loginDATA = new LoginDATA();
loginDATA.setUsername("某某某");
loginDATA.setPassword("123456");
given()
.proxy(8888)
.contentType("application/json;charset=UTF-8")
.body(loginDATA)
.when()
.post("http://localhost:8889/testlogincase");
given()
.proxy(8888)
.contentType("application/json;charset=UTF-8")
.formParam("username",loginDATA.getUsername())
.formParam("password",loginDATA.getPassword())
.when()
.post("http://localhost:8889/testlogincaseform");
}
由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。
ObjectMapperType.JACKSON_2 将message对象序列化为json。
given()
.config(RestAssured.config().encoderConfig(encoderConfig().defaultCharsetForContentType("UTF-8","application/json")))
.proxy(8888)
.body(loginDATA, ObjectMapperType.JACKSON_2)
.when()
.post("http://localhost:8889/testlogincase");
入参有中文,需要配置编码:
config(RestAssured.config().encoderConfig(encoderConfig().defaultCharsetForContentType("UTF-8","application/json")))
反序列化
让我们再次假设我们有以下的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注册一个解析器( 自定义content-type,是指程序无法解析的,如果接口中返回Content-Type:application/json 则不配置parser("application/json",Parser.JSON)系统也是可以解析的
):
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);
moco接口:
{
"description":"对象映射--反序列",
"request":{
"uri":"/getjavaobject",
"method":"get"
},
"response":{
"json":{
"username":"某某某",
"password":"123456"
}
}
},
{
"description":"对象映射--反序列,返回xml",
"request":{
"uri":"/getjavaxmlobject",
"method":"get"
},
"response":{
"text":"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<LoginDATA>\n<username>秦振霞</username>\n<password>123456</password>\n</LoginDATA>",
"headers":{
"Content-Type":"application/something"
}
}
}
实体类:
package com.course.LearnRestassuredCase;
public class LoginDATA {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
测试代码:
@Test
public void testthirtyone(){
//反序列化,返回response内容序列化为对象,可以输出对象中的属性值信息
LoginDATA loginDATA = get("http://localhost:8889/getjavaobject").as(LoginDATA.class);
System.out.println("username:"+loginDATA.getUsername()+",password:"+loginDATA.getPassword());
LoginDATA loginDATAtwo = get("http://localhost:8889/getjavaxmlobject").as(LoginDATA.class);
System.out.println("username:"+loginDATAtwo.getUsername()+",password:"+loginDATAtwo.getPassword());
//自定义content-type,是指程序无法解析的,如果接口中返回Content-Type:application/json 则不配置parser("application/json",Parser.JSON)系统也是可以解析的
//本接口中返回的是xml,如果返回json,用Parser.JSON就行
LoginDATA loginDATAthree = expect().parser("application/something",Parser.XML).when().get("http://localhost:8889/getjavaxmlobject").as(LoginDATA.class);
System.out.println("username:"+loginDATAthree.getUsername()+",password:"+loginDATAthree.getPassword());
LoginDATA loginDATAfour = expect().defaultParser(Parser.XML).when().get("http://localhost:8889/getjavaxmlobject").as(LoginDATA.class);
System.out.println("username:"+loginDATAfour.getUsername()+",password:"+loginDATAfour.getPassword());
//显示的反序列化器
LoginDATA loginDATAfive = get("http://localhost:8889/getjavaxmlobject").as(LoginDATA.class,ObjectMapperType.JAXB);
System.out.println("username:"+loginDATAfive.getUsername()+",password:"+loginDATAfive.getPassword());
}
配置
您可以使用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)。其它的默认值也可以被指定:%E3%80%82%E5%85%B6%E5%AE%83%E7%9A%84%E9%BB%98%E8%AE%A4%E5%80%BC%E4%B9%9F%E5%8F%AF%E4%BB%A5%E8%A2%AB%E6%8C%87%E5%AE%9A%EF%BC%9A),附带身份认证中的用户名和密码属性。关于设置根路径参考[这里](
RestAssured.filters(..); // List of default filtersRestAssured.requestSpecification = .. // Default request specificationRestAssured.responseSpecification = .. // Default response specificationRestAssured.urlEncodingEnabled = .. // Specify if Rest Assured should URL encoding the parametersRestAssured.defaultParser = .. // Specify a default parser for response bodies if no registered parser can handle data of the response content-typeRestAssured.registerParser(..) // Specify a parser for the given content-typeRestAssured.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")。
测试代码:
@Test
public void testthirtytwo(){
//模式复用 ResponseSpecBuilder返回校验模式复用 RequestSpecBuilder请求模式复用
//入参和返回校验有重复的可以用模式复用
//入参模式复用
RequestSpecBuilder specBuilder = new RequestSpecBuilder();
specBuilder.addParam("username","秦振霞");
specBuilder.addParam("password","123456");
specBuilder.addHeader("token","1234567890");
specBuilder.setContentType("application/json");
specBuilder.setProxy(8888);
RequestSpecification specification = specBuilder.build();
//返回body校验
ResponseSpecBuilder specBuilder1 = new ResponseSpecBuilder();
specBuilder1.expectStatusCode(200);
specBuilder1.expectBody("message",containsString("hello"));
ResponseSpecification specification1 = specBuilder1.build();
given()
.spec(specification)
.when()
.post("http://localhost:8889/testspec")
.then()
.spec(specification1);
}
过滤器
过滤器会在请求实际发起之前侦测和改变该请求的内容,也可以在响应体实际返回之前拦截并改变。您可以将其理解为AOP中的around advice(译者注:可以自行搜索切片编程)。过滤器也可以用在认证scheme、session管理、日志中。创建一个过滤器需要实现io.restassured.filter.Filter接口。使用过滤器:
given().filter(new MyFilter()). ..
rest-assured提供了几个过滤器:
-
io.restassured.filter.log.RequestLoggingFilter
: 可以打印出请求模式的细节。 -
io.restassured.filter.log.ResponseLoggingFilter
: 可以打印响应信息的细节如果响应体的状态码匹配given方法的参数。 -
io.restassured.filter.log.ErrorLoggingFilter
: 如果发生了异常(状态码在400和500之间),过滤器将会打印响应的内容。
不太明白过滤器是什么意思???
Response Builder
如果您想要通过一个过滤器改变Response ,可以使用ResponseBuilder创建一个基于原始response的新实例。比如把原始响应体改为something:
Response newResponse = new ResponseBuilder().clone(originalResponse).setBody("Something").build();
不太明白为什么要把结果改成另外一个结果??
测试代码:
Response response = given()
.spec(specification)
.when()
.post("http://localhost:8889/testspec")
.then()
.spec(specification1)
.extract()
.response();
System.out.println(response.asString());
Response newresponse = new ResponseBuilder().clone(response).setBody("oh,my god").build();
System.out.println(newresponse.asString());
日志
在大量的用例中,打印出响应或者请求的细节将有助于创建正确的预期、发送准确的请求。为此您可以使用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 bodygiven().log().params(). .. // Log only the parameters of the requestgiven().log().body(). .. // Log only the request bodygiven().log().headers(). .. // Log only the request headersgiven().log().cookies(). .. // Log only the request cookiesgiven().log().method(). .. // Log only the request methodgiven().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 lineget("/x").then().log().headers(). .. // Only log the response headersget("/x").then().log().cookies(). .. // Only log the response cookies
您也可以配置为仅当状态码匹配某个值时才打印响应体:
get("/x").then().log().ifStatusCodeIsEqualTo(302). .. // Only log if the status code is equal to 302get("/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));
moco接口:
{
"description":"路径参数",
"request":{
"uri":"/testroot",
"method":"get"
},
"response":{
"json":{
"p2pdata":{
"body":{
"firstname":"zhenxia",
"lastname":"qin",
"family":{
"one":"daddy",
"two":"mother"
}
},
"des":"happy family"
}
}
}
}
测试代码:
@Test
public void testthirtythree(){
// //使用根路径.root("p2pdata.body")
given()
.proxy(8888)
.when()
.get("http://localhost:8889/testroot")
.then()
.root("p2pdata.body")
.body("firstname",equalTo("zhenxia"));
// //配置默认的根路径
RestAssured.rootPath = "p2pdata.body";
given()
.proxy(8888)
.when()
.get("http://localhost:8889/testroot")
.then()
.body("firstname",equalTo("zhenxia"));
//在根路径上附加一些参数
given()
.proxy(8888)
.when()
.get("http://localhost:8889/testroot")
.then()
.root("%s.%s",withArgs("p2pdata","body"))
.body("firstname",equalTo("zhenxia"))
.appendRoot("%s.%s",withArgs("family","one"))
.body(withNoArgs(),equalTo("daddy"));//withNoArgs()方法是接着上面的appendRoot添加路径的基础上添加的
//拆分路径
given()
.proxy(8888)
.when()
.get("http://localhost:8889/testroot")
.then()
.root("p2pdata.body")
.body("firstname",equalTo("zhenxia"))
.detachRoot("body")//拆分路径后后root路径就是p2pdata,接下来要验证的路径就是p2pdata.des
.body("des",equalTo("happy family"));
//路径参数
// String someSubPath = "else";
// int index = 1;
// get("/x").then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..
given()
.proxy(8888)
.when()
.get("http://localhost:8889/testroot")
.then()
.root("p2pdata.body.family.%s")
.body(withArgs("one"),equalTo("daddy"))
.body(withArgs("two"),equalTo("mother"));
}
https://testerhome.com/topics/7060该文档中余下的部分内容暂不作练习。
网友评论