Rest_Assured接口测试学习
本文主要是本人学习并记录Rest_Assured测试框架的一些用法
学习过程中使用的是jdk15 maven3.8.6 以及 idea2022.2
mvn仓库地址:https://mvnrepository.com/
文章目录
前言
rest_assured测试框架是用来进行resful服务接口测试的,该框架简化了resful服务的接口测试,支持post get delete options等请求,并对响应体进行断言,支持json xml yaml excel csv等文件的结构化解析,支持 xpath jsonpath gpath等的元素定位,支持Spring,符合契约变成思想(浅谈契约式变成)
rest_assured 官方网站: https://rest-assured.io/
本文基于Junit5,学习rest_assured测试框架
一、基本用法
使用rest_assured需要引入其依赖,如下所示,将其复制到pom.xml,其中的版本号根据自己的要求填写,建议使用比较新的版本,不清楚版本号,需要使用上方mvnrepository官网地址进行搜索rest-assured
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
下面进行一个简单的get请求和post请求,restAssured主要是分为3大方法
given():设置头信息(header)设置请求参数(param,body)等
when():用来声明请求的方式(post get)和请求接口url
then():用来解析响应结果和断言(断言状态码,body体)
引入的包一定要是static修饰的静态包:import static io.restassured.RestAssured.given;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
/**
* rest-assured基本用法
*/
public class RestAssuredTest {
/**
* given:设置测试预设(请求头、请求参数、请求体等)
* when:想要执行的请求动作 get post
* then:解析结果 断言
* log().all()打印全部的相应信息
* statusCode 断言状态码
* body()断言返回的body种的信息
*/
@Test
void testGet() {
String url = "https://httpbin.ceshiren.com/get";
given()
.param("key", "value")//添加请求参数
.when()
.get(url)
.then()
.log().all()
.statusCode(200)//断言状态码
.body("origin",is(equalTo("172.17.56.110")));
}
@Test
void testPost() {
String url = "https://httpbin.ceshiren.com/post";
given().
queryParam("key","value").//参数
when().post(url).
then().log().all().statusCode(200);
}
}
二、参数化
在日常进行http接口测试时,知道,参数有很多种类型,以postman为例,body可以传输多种格式的参数,例如 xml json text 等
1.k-v型参数
get请求支持的参数类型为 key-value类型,可以将参数写在url上,也可以在写在queryparam上
post请求 也支持key-value,但是其请求参数是写在body体或者header中
传输单个参数可以使用param(key,value),传输多个参数,可以写多个param(key,value),或者使用params(k1,v1,k2,v2,…)
queryParam()方法与param方法用法类似
given()
.param("key", "value")//添加请求参数
.param("key2", "value2")//添加请求参数
//queryParam("key", "value")
//params("key","v","key2","v2")
.when()
.get(url)
.then()
.log().all()
.statusCode(200)//断言状态码
2.body数据
post大部分是使用body传输参数,因此given()中使用body()方法
- json格式数据
josn格式数据,一般在测试过程中是通过读取json文件、序列化结构体或者使用同样是k-v结构的map 来得到json
这里需要传入content-type:application/json,来声明是json结构数据
@Test
//序列化结构体为json
public void JsonTest() {
Student stu = new Student("小明", 25, "24班", 24, 2401);
String str = JSON.toJSONString(stu);
given().contentType("application/json")
.body(str)
.log().body()
.when().post(url)
.then().log().all();
}
@Test
//传输map
public void MapJsonTest() {
HashMap<String, String> map = new HashMap<>();
map.put("name","美美");
map.put("age","12");
given()
.contentType("application/json")
.body(map)
.when()
.post(url)
.then().log().all();
}
- xml格式数据
xml类型的数据,一般是通过读取xml文件来获取的,xml是一种标记型语言,通常用来定位元素例
xml型数据的用法,参考的是W3C里一个计算器的例子:http://dneonline.com/calculator.asmx
在resources文件下创建一个xml文件并引入下面内容
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Add xmlns="http://tempuri.org/">
<intA>1</intA>
<intB>1</intB>
</Add>
</Body>
</Envelope>
引入xml需要使用File读取xml文件内容,并声明content-type为text/xml类型,再将参数传入body()
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import static io.restassured.RestAssured.given;
/**
* xml格式的参数
*/
public class RestAssured3Test {
String url = "http://dneonline.com/calculator.asmx";
@Test
public void testXml() {
//定义请求体数据 xml
try {
File file = new File("src/test/resources/param.xml");
FileInputStream fis = new FileInputStream(file);
String str = IOUtils.toString(fis, "UTF-8");
given()
.contentType("text/xml")
.body(str)
.when()
.post(url)
.then().log().all().statusCode(200);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
3.form
post请求可以通过提交form表单的形式提交请求参数,这里使用given().formParams()方法来设置参数,formparams可以设置多个参数
formParam()用来设置单个参数
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
/**
* post请求
*/
public class FormTest {
String url = "https://httpbin.ceshiren.com";
@Test
void form() {
given()
//.formParam("key1","value1")
.formParams("key1", "value1", "key2", "value2")
.log().headers()
.log().body()
.when()
.post(url + "/post")
.then()
.log().body()
.statusCode(200);
}
}
三、定位响应元素并断言
测试一个接口有没有达到预期,需要进行响应的断言,这里有几种断言方式,如果返回的body是json类型可以使用rest_assurd自带或者jsonpath进行元素定位,如果返回的是xml类型则需要使用Xpath进行元素定位,定位到元素后使用assert或者hamcrest进行断言(junit5断言方式)
1. restassured 自带json提取元素方式
rest_assured测试框架自带提取json元素的方法(测试中比较常用的就是自带的方式),使用"."来定位元素并提取元素值,然后使用assert进行断言
@Test
public void jsonResp1() {
String resp = given()
.header("Hello", "world")
.when()
.get(url + "/get")
.then()
.log().all()
.statusCode(200)
.extract().response().asString();//提取响应的文本
String str = from(resp).getString("headers.Hello");//提取 headers下面的hello的值,值就是前面写的 world
assertEquals("world",str);
}
2. jsonpath查找元素
jsonpath断言元素需要引入jsonpath依赖到pom.xml中
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
</dependency>
可以使用这个地址进行练习定位json:http://jsonpath.com/
基本使用规则如下:
使用jsonpath定位元素的常用用法如下图
提取到元素后使用hamcrest匹配器进行断言
@Test
public void JsonPathTest() {
String resp = given().header("Hello", "world")
.when().get(url + "/get")
.then()
.log().all().statusCode(200)
.extract().response().asString();
String str = JsonPath.read(resp, "$.headers.Hello");//读取json中 headers下的hello
assertThat("world", is(equalTo(str)));
}
3. Xpath查找元素
xpath 查找元素的规则如下几种:
@Test
public void testXml() {
//定义请求体数据 xml
try {
File file = new File("src/test/resources/param.xml");
FileInputStream fis = new FileInputStream(file);
String reqBody = IOUtils.toString(fis, "UTF-8");
given()
.contentType("text/xml")
.body(reqBody)
.when()
.post(url)
.then()
.log().body()
.statusCode(200)
.body("//AddResult.text()", equalTo("2")); //响应断言
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
- json schema
使用json schema方式一般是用来断言整个响应的body体是否符合预期,首先是先制定一个预期的响应body体的内容和结构,使用json schema在线工具:https://app.quicktype.io/ 将预期的body体生成 json schema,如下所示:
在resources下创建一个schema.json文件将生成的json schema代码复制其中,增加使用json schema的依赖到pom.xml
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
然后发起请求,断言整个响应体
@Test
public void JsonSchema(){
given()
.when()
.get(url+"/get")
.then()
.log().all()
.statusCode(200)
//.extract().response().asString();
.assertThat()
//根据schema中的的数据断言resp body是否与匹配
.body(matchesJsonSchemaInClasspath("schema.json"));
}
四、设置请求代理
在日常工作中很多时候不能直接访问请求地址,而是需要访问代理地址,因此我们使用配置本地代理的方式来学习如何配置代理
使用工具Charles设施本地代理端口(配置代理),开启一个localhost:8888的本地代理,所有的请求都会经过代理服务
1. http请求设置代理
代码中,在given()中使用proxy()方法进行代理配置,代码如下,是进行的http请求的代理配置
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
/**
* http请求增加代理
*/
public class ProxyTest {
String url = "http://httpbin.org.com";
String proxyAddr = "127.0.0.1";
int proxyPort = 8888;
@Test
//代理是在given中增加
public void httpProxy() {
given()
.proxy(proxyAddr,proxyPort)
.when()
.get(url + "/get")
.then()
.log().all()
.statusCode(200);
}
}
2. https请求设置代理
https的代理与http有所不同,https比http多一个安全认证证书,因此直接访问https会报一个ssl的错误,因此要在代码中声明 忽略证书校验
@Test
//使用given内置的配置代理方法
public void httpsProxy() {
given()
.proxy(proxyAddr, proxyPort)
.relaxedHTTPSValidation()//忽略https证书校验
.when()
.get("https://httpbin.ceshiren.com/get")
.then().log().all()
.statusCode(200);
}
3. 全局代理
上面的方法是使用的given()中的proxy()方法,这种方法有一个缺点,就是每一个请求都需要设置一遍代理,因此引入了另一种代理方式,设置全局代理
@Test
//创建静态的代理方法
public void httpsProxy2() {
RestAssured.proxy = host(proxyAddr).withPort(proxyPort);
//忽略 tls 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
//.proxy(proxyAddr, proxyPort)
//.relaxedHTTPSValidation()//忽略https证书校验
.when()
.get("https://httpbin.ceshiren.com/get")
.then().log().all()
.statusCode(200);
}
五、headers
1.自定义header内容
使用given().headers()方法添加header:
headers中包含:user-agent,content-type、data、encoding、connection等信息,headers信息是可以自定义的
以user-agent为例,user-agent中表示的是请求客户端的信息,下面自定义user-agent
@Test
//header
public void httpsHeader() {
//定义的代理配置信息
RestAssured.proxy = host("127.0.0.1").withPort(8888);//127.0.0.1
//忽略 tls 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
.header("User-Agent", "Client-Hui","Client-Dai")//自定义 user-agent请求消息头
.when()
.get(url + "/get")
.then()
.log().all().statusCode(200);
}
@Test
//headers
public void httpsHeaders() {
//定义的代理配置信息
RestAssured.proxy = host("127.0.0.1").withPort(8888);//127.0.0.1
//忽略 tls 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
//.header("User-Agent", "client-hui","client-dai")//自定义 user-agent请求消息头
.headers("User-Agent","hui","User-Agent2","dai")
.when()
.get(url + "/get")
.then()
.log().all().statusCode(200);
}
2.设置cookie
添加请求cookie,有两种方式,一种是添加到header中
另一种是使用given()中的cookie方法,如代码所示:
@Test
//使用header添加cookie
public void AddCookieByHeader() {
//定义的代理配置信息
RestAssured.proxy = host("127.0.0.1").withPort(8888);//127.0.0.1
//忽略 tls 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
.log().all()
.headers("Cookie", "cookie-value")
.when()
.get(url + "/get")
.then()
.log().all().statusCode(200);
}
@Test
//使用cookie添加cookie
public void AddCookie() {
//定义的代理配置信息
RestAssured.proxy = host("127.0.0.1").withPort(8888);//127.0.0.1
//忽略 tls 证书校验
RestAssured.useRelaxedHTTPSValidation();
given()
//.cookie("Cookie","cookies-value")
.cookies("cookie1", "cookie1", "cookie2", "cookie2")
.when()
.get(url + "/get")
.then()
.log().all().statusCode(200);
}
五、设置超时时间
分为下面的步骤
- 创建HttpClientConfig实例,并设置超时时间(单位毫秒)
- 创建RestAssuredConfig实例,将上面设置超时时间的HttpClientConfig注入到RestAssuredConfig中
- given中调用config,引入RestAssuredConfig
下面用例中,case2将会超时 case1和case3顺利执行
import io.restassured.RestAssured;
import io.restassured.config.HttpClientConfig;
import io.restassured.config.RestAssuredConfig;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
/**
* 设置超时
*/
public class TimeOutTest {
@BeforeAll
public static void setupClass() {
RestAssured.baseURI = "https://httpbin.ceshiren.com";
}
@Test
void case1() {
given()
.when()
.get("/get")
.then();
}
@Test
//阻塞
void case2() {
//设置http客户端请求超时时间 单位毫秒 参数名为 http.socket.timeout
HttpClientConfig clientConfig = HttpClientConfig.httpClientConfig()
.setParam("http.socket.timeout", 3000);
//设置RestAssuredConfig 注入创建的httpConfig
RestAssuredConfig timeout = RestAssuredConfig.config().httpClient(clientConfig);
given()
.config(timeout)//配置前面创建的RestAssured
.when()
.get("/delay/10")//该接口延迟10s
.then();
}
@Test
void case3() {
given()
.when()
.get("/get")
.then();
}
}
六、上传文件
使用given()中multiPart()上传文件,multiPart不仅可以用来上传文件,还可以填写key-value形式的参数
multipart可以用来上传多个文件,需要写多个multipart
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.File;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
/**
* 上传文件学习
*/
public class UploadFileTest {
@BeforeAll
static void setupClass(){
RestAssured.baseURI = "https://httpbin.ceshiren.com";//设置公共域名
RestAssured.proxy = host("localhost").withPort(8888);//设置代理
RestAssured.useRelaxedHTTPSValidation();
}
@Test
void uploadFile(){
File file = new File("D:\\test\\test.txt");//加载文件
given()
.multiPart("test file",file)//文件名 文件
.
.log().all()
.when()
.post("/post")
.then()
.log().body()
.statusCode(200);
}
}
上面的请求发送后,抓包得到的信息如下
上传文件的header中,content-type:multipart/form-data
七、操作jdbc
这里操作mysql因此需要引入mysql驱动的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
想要连接数据库mysql需要知道数据库的访问地址以及用户名密码,一般不同的用户拥有不同的权限,考虑道实际工作场景,操作数据库比较敏感,尤其是业务复杂,表关联复杂时,操作数据库进行增删改容易造成问你题,测试过程一般只能拥有只读权限,下面代码根据访问的地址、用户名、密码 创建数据库连接
url=“jdbc:mysql://访问地址:端口/哪一个数据库”
username=“用户名”
password=“密码”
建一个工具类来创建jdbc连接
public class DatabaseUtils {
Connection connection = null;
Statement statement = null;
public DatabaseUtils(String url, String username, String password) {
try {
connection = DriverManager.getConnection(url, username, password);
statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
1.简单查询
使用代码来进行查询:
下面使用sql查询数据库信息,并获取信息
下面sql根据name查Id,这里查询的结果只返回一个值,代码如下
select id from table where name = 1
public Integer queryIdBySql(String sql) {
Integer res;
try {
ResultSet resultSet = statement.executeQuery(sql);
resultSet.first();
res = resultSet.getInt("id");
} catch (SQLException e) {
throw new RuntimeException(e);
}
return res;
}
2.进阶版查询
上面代码看的出来,查询的参数是id,是一个int型,在日常实际使用中返回的参数什么类型都可能有,因此需要写一个通用的方法
这里采用JsonPath来提取参数的形式实现这个方法,因此需要将数据库查询得到的数据弄成Json
因此用到提取Json的依赖
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.11.11</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
<scope>test</scope>
</dependency>
先查询得到结果集,再将结果集中的数据遍历放入到List中,再将数据转换为Json
public String getResultSetBySQL(String sql_query) {
// 格式转换将ResultSet 转换为 标准的json格式,方便完成断言。
try {
ResultSet resultSet = statement.executeQuery(sql_query);
// 获取所有
ResultSetMetaData md = resultSet.getMetaData();
int numCols = md.getColumnCount();
// 获取所有的数据表列名
List<String> colNames = new ArrayList<>();
for (int i = 1; i <= numCols; i++) {
String name = md.getColumnName(i);
colNames.add(name);
}
// 返回一个json结构的查询结果
return DSL.using(connection)
.fetch(resultSet)
.map(new RecordMapper() {
@Override
public JSONObject map(Record r) {
JSONObject obj = new JSONObject();
colNames.forEach(cn -> obj.put(cn, r.get(cn)));
return obj;
}
}).toString();
} catch (SQLException e) {
e.printStackTrace();
}
// 如果异常,return 空 json,不影响后续逻辑执行
return "{}";
}
八、加解密
1.Base64
base64的加解密使用的是 org.apache.commons.codec.binary.Base64
加密使用Base64.encodeBase64String方法,将一个字节数据加密得到密文字符串
解密是使用Base64.decodeBase64方法,将一串密文字符串,解密成字节数组,解密后需要将字节数组转换成字符串才能得到明文,要注意的是,容易出现乱码,因此要使用标准的编码字符类型,这里用Utf-8
@Test
void encryBase64() {
//加密铭文 hogwarts
byte[] arr = "test".getBytes(StandardCharsets.UTF_8);
String encode = Base64.encodeBase64String(arr);
System.out.println("密文:" + encode);
//解密
byte[] res = Base64.decodeBase64(encode);
String decode = new String(res,StandardCharsets.UTF_8);
System.out.println("明文:" + decode);
}
总结
以上是rest_assured框架进行resful接口测试的基本用法