Java 接口自動化測試首選方案:REST Assured 實踐 (一)

在 REST Assured 的官方 GitHub 上有這樣一句簡短的描述: Java DSL for easy testing of REST services 簡約的 REST 服務測試 Java DSL
REST Assured 官方的 README 第一句話對進行了一個優(yōu)點的概述,總的意思表達的就是簡單好用。那么 REST Assured 有哪些優(yōu)點,又該如何使用呢?
用 Java 做接口自動化測試首選 REST Assured,具體原因如下:
開源
簡約的接口測試 DSL
支持 xml json 的結(jié)構(gòu)化解析
支持 xpath jsonpath gpath 等多種解析方式
對 spring 的支持比較全面
添加 maven 依賴
<dependency>?
? <groupId>io.rest-assured</groupId>
??
?<artifactId>rest-assured</artifactId>?
? ?<version>4.0.0</version>
??
?<scope>test</scope>
</dependency>
我們對接口進行測試一般由三步曲:傳參、發(fā)請求、響應結(jié)果斷言,REST Assured給我們提供了清晰的三步曲,以given、when、then的結(jié)構(gòu)來實現(xiàn),基本寫法如下:
//使用參數(shù)?
given().?
? ?param("key1", "value1").
?
?param("key2", "value2").
when().
?
??post("/somewhere").
then().?
? ?body(containsString("OK"))?
?//使用X-Path (XML only)?
given().
?
??params("firstName", "John", "lastName", "Doe").?
when().
??
?post("/greetMe").?
then().
??
?body(hasXPath("/greeting/firstName[text()='John']"))
請求體body如下
{
?
"password": "elcrD28ZSLLtR0VLs/jERA\u003d\u003d\n",?
??"grant_type": "password",?
??"scope": "server",?
??"userType": 1,
?
"username": "xxx"
}
Request Header 如下:
Headers: ??
?Authorization=Basic c3lzdGVtxxxRlbQ==
??
? ? ?Host=47.103.xxx.133
??
? ? ?Accept=*/*
? ?
? ?Content-Type=application/json; charset=ISO-8859-1
我們發(fā)送請求經(jīng)常需要帶有參數(shù),使用 given() 就可以實現(xiàn),當時當我們使用 given() 的時候發(fā)現(xiàn)其中有很多傳參方法如下:
沒錯,在傳參的方法中包含了 param、pathParam、queryParam 和 formParam,下面來研究下這幾個傳參方法的區(qū)別
param
通常我們都會使用 given().param 方法來傳參,REST Assured 會根據(jù) HTTP 方法自動嘗試確定哪種參數(shù)類型(即查詢或表單參數(shù)),如果是 GET,則查詢參數(shù)將自動使用,如果使用 POST,則將使用表單參數(shù);
queryParam 和 formParam
有時候在 PUT 或 POST 請求中,需要區(qū)分查詢參數(shù)和表單參數(shù)時,就需要使用queryParam 和 formParam 方法了,具體寫法如下:
given().
??
? ? formParam("formParamName", "value1").
? ? ? queryParam("queryParamName", "value2").?
when().
? ??
? post("/something")
pathParam 使用given時指定請求路徑的參數(shù),這個方法很少用到,或者說我本人幾乎沒用到過(可能我的修行還不夠,踩坑還太少~);具體寫法如下:
given().
??
? ? ?pathParam("OAuth", "oauth").
? ?
?? ?pathParam("accessToken", "token").?
when().
? ?
?? ?post("/auth/{OAuth}/{accessToken}").
then().
??
? ? ? ..
header/headers 經(jīng)常還需要在請求頭中帶入?yún)?shù),這個時候就可以使用header或headers方法,寫法如下:
given()
? ?
?? .header("Authorization","Basic c3lzdGVtOxxxbQ==")
? ? ? .header("Host","47.xxx.xxx.133")
或者用headers將多個參數(shù)寫在一起:
given()
? ? ?
.headers("Authorization","Basic c3lzdGVtxxx3RlbQ==","Host","47.xxx.xxx.133")
cookie 有時候需要在請求中帶入cookie,restassured提供了cookie方法來實現(xiàn):
given()
? ?
?.cookie("c_a","aaaaaa")
?
?? ?.cookie("c_b","bbbbbb"). ..
contentType 經(jīng)常還會設(shè)置contentType,最常見的就是application/json了,寫法如下:
given().contentType("application/json"). ..
//或者
given().contentType(ContentType.JSON). ..
body 在POST, PUT 或 DELETE請求中,我們經(jīng)常還需要帶上請求體body,寫法如下:
given().body("{\n" +
? ?
?? ?"\t\"password\": \"elcrD28xxxR0VLs/jERA\\u003d\\u003d\\n\",\n" +
? ? ? ? ? ? ? ?"\t\"grant_type\": \"password\",\n" +
? ? ? ? ? ? ? ?"\t\"scope\": \"server\",\n" +
? ?
? ? ? ? ? ?"\t\"userType\": 1,\n" +
? ? ?
?? ? ? ? ?"\t\"username\": \"xxx\"\n" +
?
? ? ? ? ? ? ?"}")
也可以用request更為明確的指出是請求body:
given().request().body("{\n" +
? ??
? ?? ?"\t\"password\": \"elcrD28xxxR0VLs/jERA\\u003d\\u003d\\n\",\n" +
? ? ? ? ? ? ? ?"\t\"grant_type\": \"password\",\n" +
? ? ? ? ? ? ? ?"\t\"scope\": \"server\",\n" +?
? ? ? ? ? ? ? ?"\t\"userType\": 1,\n" +
? ? ?
?? ? ? ? ?"\t\"username\": \"xxx\"\n" +
? ?
?? ? ? ? ? ?"}")
沒有參數(shù) 如果我們沒有參數(shù)需要傳遞,也可以省略掉given():
get("/lotto").then().assertThat().body("lotto.lottoId", equalTo(5));
proxy 有時候我們需要進行接口的調(diào)試,抓包是最常用的一種方式,rest-assured 提供了 proxy 方法,可以設(shè)置代理,寫法如下:
given().proxy("127.0.0.1",8888). ..
實際運行結(jié)果:
when主要用來觸發(fā)請求,在when后面接著請求URL:
given().when().post("http://47.103.xxx.133/auth/oauth/token"). ..
前面在 given 中我們設(shè)置了很多請求參數(shù),在 when 中也可以設(shè)置,只不過要注意的是在請求之前設(shè)置;這也比較好理解,如果再請求之后的話,參數(shù)都設(shè)置怎么發(fā)請求呢?
given()
??
?.when()
?
?? ? ?.contentType(ContentType.JSON)
? ? ? ?.headers("Authorization","Basic c3lzxxx3RlbQ==","Host","47.xxx.xxx.133")
? ? ? ?.request().body("{\n" +
? ?
? ? ? ?"\t\"password\": \"elcrD28ZSLLtR0VLs/jERA\\u003d\\u003d\\n\",\n" +
? ? ? ? ? ?"\t\"grant_type\": \"password\",\n" +
??
? ? ? ? ?"\t\"scope\": \"server\",\n" +
? ??
? ? ? ?"\t\"userType\": 1,\n" +
? ? ? ?
?? ?"\t\"username\": \"qinzhen\"\n" +
? ?
?? ? ? ?"}")
? ?
?? ?.post("http://47.xxx.xxx.133/auth/oauth/token")
?
?? . ..
斷言-then().body() then().body() 可以對響應結(jié)果進行斷言,在 body 中寫入斷言:
.. post("http://47.xxx.xxx.133/auth/oauth/token")
? .then().statusCode(200).body("code",equalTo(1));
其中statusCode(200)是對狀態(tài)碼的斷言,判斷狀態(tài)碼是否為200; body(“code”,equalTo(1))是對返回體中的 code 進行斷言,要求返回 code值為1 。
實操演示: 我們將上述的 given、when、then 結(jié)合起來看一下實際運行效果,這里在運行之前再提一個功能,我們可以在 when 和 then 后面加上.log().all(),這樣在運行過程中就可以把請求和響應的信息都打印出來:

獲取響應-then().extract().body().path(“code”) 我們可以在 then 后面利用 .extract().body() 來獲取我們想要 body 的返回值,它們也可以直接接在斷言后面,寫法如下:
.. .then()
? ?
? ?.log().all().statusCode(200).body("code",equalTo(1))
? ? ? ?.extract().body().path("code");
實操演示: 演示前再來看一個新的功能,上面我們再寫請求體 body 時時這樣的:
body("{\n" +
? ?
"\t\"password\": \"elcrD28ZxxxVLs/jERA\\u003d\\u003d\\n\",\n" +
? ?"\t\"grant_type\": \"password\",\n" +
?
?"\t\"scope\": \"server\",\n" +
?
?"\t\"userType\": 1,\n" +
?
?"\t\"username\": \"qinzhen\"\n" +
?
??"}")
看起來有點丑,改造一下;rest-assured 為我們提供了一個利用 HashMap 來創(chuàng)建json 文件的方法,先把要傳的字段放入 hashmap 中,然后用 contentType 指明JSON 就可以了,具體寫法如下:
HashMap map = new HashMap();
map.put("password","elcrD28ZSLLtR0VLs/jERA\u003d\u003d\n");
map.put("grant_type","password");
map.put("scope","server");
map.put("userType",1);
map.put("username","xxx");
given()
? ? ?.headers("Authorization","Basic c3lzdGVtxxxlbQ==","Host","47.xxx.xxx.133")
? ? ?.contentType(JSON)
? ? ?.body(map). ..
現(xiàn)在進行完整的請求,獲取返回值 code 并打?。?/p>
HashMap map = new HashMap();
map.put("password","elcrD28ZSLLtR0VLs/jERA\u003d\u003d\n");
map.put("grant_type","password");
map.put("scope","server");
map.put("userType",1);
map.put("username","xxx");
Integer code =
given()
? ?.headers("Authorization","Basic c3lzdGVtxxxlbQ==","Host","47.xxx.xxx.133")
? ?.contentType(JSON)
? ?.body(map).
when()
? ?.log().all().post("http://47.xxx.xxx.133/auth/oauth/token").
then()
? ?.log().all().statusCode(200).body("code",equalTo(1))
? ?.extract().body().path("code");
System.out.println("返回code的值是:"+code);
運行結(jié)果:

關(guān)于REST Assured,這里僅僅算是初步認識。認識它的語法結(jié)構(gòu)和功能,對于更多豐富的用法還需要慢慢探索研究,特別是斷言的部分,是測試工程師最常用最終要的功能之一。REST Assured提供的完整斷言手段,在后續(xù)文章中我們一起探討。
對于想系統(tǒng)進階提升測試開發(fā)技能的同學,推薦霍格沃茲測試學院出品的 《測試開發(fā)從入門到高級實戰(zhàn)》系統(tǒng)進階班課程。