美文网首页
RestAssured学习(三)

RestAssured学习(三)

作者: 零下的雨 | 来源:发表于2018-12-29 15:57 被阅读0次

    验证响应数据

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

    响应体

    请参阅使用示例,例如JSONXML.

    您还可以将响应正文映射到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:&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会按照以下的优先级进行序列化:

    使用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提供了几个过滤器:

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

    不太明白为什么要把结果改成另外一个结果??

    测试代码:

            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该文档中余下的部分内容暂不作练习。

    相关文章

      网友评论

          本文标题:RestAssured学习(三)

          本文链接:https://www.haomeiwen.com/subject/dfhslqtx.html