接口测试进阶
请求构造
GET /stock/search.json?code=sogo HTTP/1.1
Accept: */*
Host: xueqiu.com
Connection: close
User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_51)
Accept-Encoding: gzip, deflate
真实的浏览器请求
GET /stock/search.json?code=sogo HTTP/1.1
Host: xueqiu.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Referer: https://xueqiu.com/k?q=sogo
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: device_id=864d4cb52ace61737d69da102e7e996d; _gid=GA1.2.255364479.1537070278; _gat_gtag_UA_16079156_4=1; Hm_lpvt_1db88642e346389874251b5a1eded6e3=1537070292
Get请求常见的请求构造方法
@Test
public void request(){
useRelaxedHTTPSValidation();
given()
.proxy("127.0.0.1", 8080)
.log().all()
.queryParam("code", "sogo")
.header("user-agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36")
.header("Accept", "application/json, text/plain, */*")
.header("Referer", "https://xueqiu.com/k?q=sogo")
.cookie("xq_a_token", "9c75d6bfbd0112c72b385fd95305e36563da53fb")
.when()
.get("https://xueqiu.com/stock/search.json")
.then()
.log().all()
.statusCode(200)
;
}
如果构造json请求需要依赖里添加json相关的库,gson或者jackson都可以
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
``
## 断言机制
JsonPath: http://static.javadoc.io/io.rest-assured/json-path/3.1.1/io/restassured/path/json/JsonPath.html
```java
package offline3;
import org.junit.Test;
import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;
public class TesterHomeTest {
@Test
public void demo(){
useRelaxedHTTPSValidation();
int limit=10;
given()
.queryParam("limit", limit)
.when()
.get("https://testerhome.com/api/v3/topics.json")
.then()
.statusCode(200)
.body("topics.size()", equalTo(3+limit))
.body("topics[0].id", equalTo(16142))
.body("topics.find { x -> x.id > 16141 }.title ",
equalTo("技术沙龙招募:从研发到测试,手把手教你打造绿色应用"))
.body("topics.findAll { x -> x.id > 16141 }.title[0] ",
equalTo("技术沙龙招募:从研发到测试,手把手教你打造绿色应用"))
.body("topics.findAll{ it.title == '技术沙龙招募:从研发到测试,手把手教你打造绿色应用'}.size()",
equalTo(1))
.body("topics.title", hasItems(
"技术沙龙招募:从研发到测试,手把手教你打造绿色应用",
"美团技术沙龙北京站:千万级日活 App 的质量保证"))
;
}
}
断言失败继续下一个断言
package offline3;
import io.restassured.response.Response;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.useRelaxedHTTPSValidation;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
public class AssertionTest {
@Rule
public ErrorCollector collector = new ErrorCollector();
@Test
public void assertionThats(){
assertThat(1, equalTo(2));
assertThat(2, equalTo(2));
assertThat(3, equalTo(2));
}
@Test
public void assertions(){
collector.checkThat(1, equalTo(2));
collector.checkThat(2, equalTo(2));
collector.checkThat(3, equalTo(2));
}
@Test
public void extract(){
useRelaxedHTTPSValidation();
int limit=10;
Response response=given().proxy("127.0.0.1", 8080)
.queryParam("limit", limit)
.when()
.get("https://testerhome.com/api/v3/topics.json")
.then()
.statusCode(200)
.extract().response();
System.out.println(response.path("topics[0]").toString());
System.out.println((Integer) response.path("topics[-1].id"));
}
@Test
public void assertion(){
useRelaxedHTTPSValidation();
int limit=10;
Response response=given().proxy("127.0.0.1", 8080)
.queryParam("limit", limit)
.when()
.get("https://testerhome.com/api/v3/topics.json")
.then()
.extract().response();
collector.checkThat(response.statusCode(), equalTo(400));
collector.checkThat(response.path("topics.size()"), equalTo(3+limit));
collector.checkThat(response.path("topics[0].id"), equalTo(16142));
collector.checkThat(response.path("topics.find { x -> x.id > 16141 }.title "),
equalTo("技术沙龙招募:从研发到测试,手把手教你打造绿色应用"));
collector.checkThat(response.path("topics.findAll { x -> x.id > 16141 }.title[0] "),
equalTo("技术沙龙招募:从研发到测试,手把手教你打造绿色应用_error"));
collector.checkThat(response.path("topics.findAll{ it.title == '技术沙龙招募:从研发到测试,手把手教你打造绿色应用'}.size()"),
equalTo(1));
collector.checkThat(response.path("topics.title"), hasItems(
"技术沙龙招募:从研发到测试,手把手教你打造绿色应用",
"美团技术沙龙北京站:千万级日活 App 的质量保证"));
;
}
}
返回值类型设置
@Test
public void quote(){
given()
.config(RestAssuredConfig.config().jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)))
.queryParam("symbol", "SH000001,HKHSI,.INX")
.cookie("xq_a_token", "9c75d6bfbd0112c72b385fd95305e36563da53fb")
.when()
.get("https://stock.xueqiu.com/v5/stock/batch/quote.json")
.then()
.log().all()
.statusCode(200)
.body("data.items.quote.find { it.symbol == 'SH000001'}.name", equalTo("上证指数"))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current", equalTo( 2681.64))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current", closeTo( 2681.64, 100))
;
}
调用函数转换
@Test
public void quote(){
given()
//.config(RestAssuredConfig.config().jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)))
.queryParam("symbol", "SH000001,HKHSI,.INX")
.cookie("xq_a_token", "9c75d6bfbd0112c72b385fd95305e36563da53fb")
.when()
.get("https://stock.xueqiu.com/v5/stock/batch/quote.json")
.then()
.log().all()
.statusCode(200)
.body("data.items.quote.find { it.symbol == 'SH000001'}.name", equalTo("上证指数"))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current", equalTo( 2681.64f))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current.toDouble()", closeTo( 2681.64, 100))
;
}
认证
basic测试网址:http://jenkins.testing-studio.com:9001/
FIlter机制
修改请求,自动添加cookie、header,以及自动更新的token
@Test
public void requestFilter() {
filters((new Filter() {
@Override
public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
System.out.println("request filter");
System.out.println(requestSpec.getQueryParams());
requestSpec.queryParam("symbol", "SH000002");
Response response = ctx.next(requestSpec, responseSpec);
System.out.println("response filter");
return response;
}
})
);
given()
.log().all()
//.config(RestAssuredConfig.config().jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)))
.queryParam("symbol", "SH000001,HKHSI,.INX")
.cookie("xq_a_token", "9c75d6bfbd0112c72b385fd95305e36563da53fb")
.when()
.get("https://stock.xueqiu.com/v5/stock/batch/quote.json")
.then()
.log().all()
.statusCode(200)
.body("data.items.quote.find { it.symbol == 'SH000001'}.name", equalTo("上证指数"))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current", equalTo(2681.64f))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current.toDouble()", closeTo(2681.64, 100))
;
}
修改响应,可以用于接口解密,响应头信息的修改,以及其他协议的测试。
@Test
public void reponseFilter(){
filters((new Filter() {
@Override
public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) {
Response response = ctx.next(requestSpec, responseSpec);
System.out.println("origin response");
System.out.println(response.getBody().asString());
System.out.println("decode response");
String contentDecode=new String(Base64.decodeBase64(response.getBody().asString()));
System.out.println(contentDecode);
Response newResponse = new ResponseBuilder().clone(response)
.setContentType(ContentType.JSON)
.setBody(contentDecode)
.build();
System.out.println("response filter");
System.out.println(newResponse.getBody().asString());
return newResponse;
}
})
);
given()
.proxy("127.0.0.1", 8080)
.auth().basic("hogwarts", "123456")
.when()
.get("http://jenkins.testing-studio.com:9001/base64.json")
.then()
.log().all()
.statusCode(200)
.body("data.items.quote.find { it.symbol == 'SH000001'}.name", equalTo("上证指数"))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current", equalTo(2681.64f))
.body("data.items.quote.find { it.symbol == 'SH000001'}.current.toDouble()", closeTo(2681.64, 100))
;
}
数据驱动
用例维护
作业
- https://testerhome.com/api/v3/topics.json?limit=20 从这个接口中找出来title是“美团技术沙龙北京站:千万级日活 App 的质量保证”的作者
- 用自己的浏览器访问雪球,找到quote.json接口,对它进行基础的一个查询测试,断言对应的股票的基础属性。
- 实现自动登陆jenkins,并自动更新每个后续请求的cookie,以触发demo job的构建为例 http://jenkins.testing-studio.com:8081/job/demo/
- 实现http://jenkins.testing-studio.com:9001/base64base64.json 解密并进行结果断言
相关数据
POST /j_acegi_security_check HTTP/1.1
Host: jenkins.testing-studio.com:8081
Connection: keep-alive
Content-Length: 311
Pragma: no-cache
Cache-Control: no-cache
Origin: http://jenkins.testing-studio.com:8081
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://jenkins.testing-studio.com:8081/login?from=%2F
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: jenkins-timestamper-offset=-28800000; _ga=GA1.2.2053923289.1527161186; screenResolution=1440x900; JSESSIONID.2982ccf6=node0fmmrgi81enzz3ga6av8u1r9h36.node0
j_username=demo&j_password=demo&from=%2F&Jenkins-Crumb=fbb99db66b17e444aae303caa1076982&json=%7B%22j_username%22%3A+%22demo%22%2C+%22j_password%22%3A+%22demo%22%2C+%22remember_me%22%3A+false%2C+%22from%22%3A+%22%2F%22%2C+%22Jenkins-Crumb%22%3A+%22fbb99db66b17e444aae303caa1076982%22%7D&Submit=%E7%99%BB%E5%BD%95
触发job构建的命令
curl 'http://jenkins.testing-studio.com:8081/job/demo/build' \\
-H 'Origin: http://jenkins.testing-studio.com:8081' \\
-H 'Cookie: jenkins-timestamper-offset=-28800000; _ga=GA1.2.2053923289.1527161186; screenResolution=1440x900; JSESSIONID.2982ccf6=node0oegox25ng1u7e971e3gah7mw37.node0' \\
--data 'Jenkins-Crumb=d8f8afd502f2e5eeb19cbb5f8482a15b&json=%7B%22Jenkins-Crumb%22%3A+%22d8f8afd502f2e5eeb19cbb5f8482a15b%22%7D' \\
--compressed