一、概括:
一、简介:
- 是由 Java 实现的 REST API 测试框架
- 支持发起 POST,GET,PUT,DELETE 等请求
- 可以用来验证和校对响应信息
- 官网地址: http://rest-assured.io
二、优势
- 简约的接口测试 DSL
- 支持 xml/json 的结构化解析
- 支持xpath/jsonpath/gpath解析方式
- 对 spring 的支持比较全面
- 符合契约编程思想
三、 环境准备
- 基于 JDK 11、JUnit5
- 创建 maven 项目
- pom.xml 添加 rest-assured 的依赖
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
二、实战
一、新建一个测试类 TestRestAssured.java
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestRestAssured {
@Test
void fun(){
given()
.header("Hello", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all();
}
附录:JUnit5 依赖配置
<!-- JUnit5 -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
二、接口请求构造
语法格式 given():可以设置测试预设 param():URL 查询参数 when():所要执行的操作 get():GET 请求 post():POST 请求 then():可以解析结果、断言 statusCode():响应状态码断言
import static io.restassured.RestAssured.given;
import org.junit.jupiter.api.Test;
public class TestRestAssuredGet {
@Test
void testGet(){
given()
.param("username", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
}
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestRestAssuredPost {
@Test
void testPost(){
given()
.param("username", "Hogwarts")
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.log().all()
.statusCode(200);
}
直接拼接在 URL 中 ?username=Hogwarts&id=666 GET 请求 param():查询参数 POST 请求 queryParam():查询参数 formParam():表单参数
import static io.restassured.RestAssured.given;
import org.junit.jupiter.api.Test;
public class TestRestAssuredGet {
@Test
void testGet(){
given()
.param("username", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
三、接口测试断言
问题: 如何确保请求可以发送成功 如何保证符合业务需求 解决方案: 响应断言
响应结果类型:
类型 | 断言方法 | 含义 |
---|
状态码 | statusCode() | 响应状态码 | 响应头 | header() | 响应头信息 | 内容 | body() | 内容完全匹配 |
import static io.restassured.RestAssured.given;
import org.junit.jupiter.api.Test;
public class TestAssertionStatusCode {
@Test
void testStatusCode(){
given()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.IsEqual.equalTo;
public class TestAssertionBody {
@Test
void testBody(){
given()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.body("origin", equalTo("113.89.246.184"));
}
}
提问:若碰到复杂断言应该如何处理?
-
jsonpath -
jsonschema -
自行编写解析算法 -
附录:Hamcrest 依赖配置
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
四、json/xml请求
1、简介
2、json请求
1、构造 JSON 请求体
- JSON 字符串
- HashMap 对象 + Jackson库
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestJsonStr {
@Test
void testJsonRequest(){
String jsonStr = "{\"Hello2\": \"Hogwarts\"}";
given()
.contentType("application/json")
.body(jsonStr)
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
import java.util.HashMap;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestJsonObj {
@Test
void testJsonRequest(){
HashMap<String, String> jsonObj = new HashMap<>();
jsonObj.put("Hello", "Hogwarts");
given()
.contentType("application/json")
.body(jsonObj)
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
}
}
2、XML
简介
- 是 eXtensible Markup Language 的缩写
- 是 可扩展标记语言,类似 HTML
- 是用来传输和存储数据
- 是通过 < > 标签来描述信息
- 是 W3C 的推荐标准
<?xml version="1.0"?>
<study>
<course>
<name>JUnit5测试框架</name>
<school>Hogwarts</school>
</course>
<course>
<name>接口自动化测试</name>
<school>Hogwarts</school>
</course>
</study>
xml请求
构建 XML 请求体
- 外部 XML 文件
- 字符串
- 示例接口地址:http://dneonline.com/calculator.asmx
- 外部 XML 文件
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Add xmlns="http://tempuri.org/">
<intA>1</intA>
<intB>1</intB>
</Add>
</Body>
</Envelope>
import static io.restassured.RestAssured.*;
import org.junit.jupiter.api.Test;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class TestXML {
@Test
void testSoapApi() throws IOException {
File file = new File("src/test/resources/add.xml");
FileInputStream fis = new FileInputStream(file);
String reqBody = IOUtils.toString(fis, "UTF-8");
given()
.contentType("text/xml")
.body(reqBody)
.log().headers()
.log().body()
.when()
.post("http://dneonline.com//calculator.asmx")
.then()
.statusCode(200);
}
}
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
<scope>test</scope>
</dependency>
五、json/xml响应断言
1、XML 响应断言
-
XPath简介 -
是 XML 路径语言 -
是 XML Path Language 的缩写 -
是用来确定 XML 文档中某部分位置 -
XPath 语法
Xpath | 描述 |
---|
/ | 根节点 | . | 现行节点 | // | 不管位置,选择所有符合条件的元素 | * | 匹配所有元素节点 | [ ] | 迭代器标示 | | | 支持迭代器中做多选 |
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Add xmlns="http://tempuri.org/">
<intA>1</intA>
<intB>2</intB>
</Add>
</Body>
</Envelope>
@Test
void readXml() {
File file = new File("C:\\Users\\86189\\IdeaProjects\\java\\src\\main\\resources\\2.xml");
FileInputStream is = null;
try {
is = new FileInputStream(file);
String result = IOUtils.toString(is, "UTF-8");
given().contentType("text/xml").body(result)
.when()
.post("http://dneonline.com/calculator.asmx")
.then()
.body("//AddResult.text()",equalTo("3")).log().all();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2、json响应断言
json简介
json响应数据
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip,deflate",
"Hello": "Hogwarts",
"Host": "httpbin.ceshiren.com",
"User-Agent": "Apache-HttpClient/4.5.13 (Java/11.0.13)",
"X-Forwarded-Host": "httpbin.ceshiren.com",
"X-Scheme": "https"
},
"origin": "113.89.246.226",
"url": "https://httpbin.ceshiren.com/get"
}
jsonpath简介
- 是一种查询语言
- 是用来解析 JSON 数据
- 项目地址:https://github.com/json-path/JsonPath
jsonpath语法
JSONPath | 描述 |
---|
$ | 根节点 | @ | 现行节点 | … | 不管位置,选择所有符合条件的元素 | * | 匹配所有元素节点 | . | 取子节点 | [] | 取子节点,支持名称或者下标 | [,] | 支持迭代器中做多选 | ?() | 支持过滤操作 |
语法示例
- 使用点号
$.address.city $.phoneNumbers[0].number $.phoneNumbers[*].number $…number - 使用中括号
$[address][city] $[phoneNumbers][0][number] - 过滤条件
$.phoneNumbers[?(@.type == ‘iPhone’)].number
JSONPath 响应断言
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.path.json.JsonPath.from;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestJSONPathNested {
@Test
void fun() {
String resp = given()
.header("Hello", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().body()
.extract().response().asString();
String word = from(resp).getString("headers.Hello");
System.out.println(word);
assertEquals("Hogwarts", word);
package ch06;
import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestJSONPathStandalone {
@Test
void fun() {
String resp = given()
.header("Hello", "Hogwarts")
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.extract().response().asString();
String word = JsonPath.read(resp, "$.headers.Hello");
System.out.println(word);
assertEquals("Hogwarts", word);
}
}
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
<scope>test</scope>
</dependency>
六、Headers/Cookie 处理
1、Headers 简介
- HTTP Headers 也叫做 HTTP 消息头
- 允许客户端和服务器传递附加信息
- 由名称、冒号、具体的值组成
- 设置请求 Headers
package ch09;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestHeader {
@Test
void testSetHeader() {
RestAssured.proxy = host("localhost").withPort(8888);
given()
.header("User-Agent", "hogwarts")
.relaxedHTTPSValidation()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
}
}
2、Cookie 简介
添加 Cookie 的两种方式
- 通过 header() 方法
- 通过 cookie() 方法
- 设置请求 Cookie
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestCookieByHeader {
@Test
void testAddCookieByHeader() {
RestAssured.proxy = host("localhost").withPort(8888);
given()
.header("Cookie", "my_cookie1=hogwarts")
.relaxedHTTPSValidation()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
}
}
package ch09;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestCookie {
@Test
void testAddCookie() {
RestAssured.proxy = host("localhost").withPort(8888);
given()
.cookie("my_cookie", "hogwarts")
.relaxedHTTPSValidation()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
}
}
七、form请求
1、form 表单请求简介
- application/x-www-form-urlencoded
应用场景 - 数据量不大
- 数据层级不深的情况
- 通常以键值对传递
- form 表单请求的使用
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestFormParam {
@Test
void testFormParam() {
RestAssured.proxy = host("localhost").withPort(8888);
RestAssured.useRelaxedHTTPSValidation();
given()
.formParam("username", "hogwarts")
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
}
}
public class TestFormParams {
@Test
void testFormParams() {
RestAssured.proxy = host("localhost").withPort(8888);
RestAssured.useRelaxedHTTPSValidation();
given()
.formParams("username", "hogwarts",
"pwd", "666")
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
}
}
八、超时处理
- 1、为什么需要请求超时处理
- 设置请求超时的效果
- 设置超时时间:超时后会停下来,然后报错
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 TestTimeout {
@BeforeAll
static void setupClass(){
RestAssured.baseURI = "https://httpbin.ceshiren.com";
}
@Test
void case1() {
given()
.when()
.get("/get")
.then()
.statusCode(200);
}
@Test
void case2(){
HttpClientConfig clientConfig = HttpClientConfig
.httpClientConfig()
.setParam("http.socket.timeout", 3000);
RestAssuredConfig myTimeout = RestAssuredConfig
.config()
.httpClient(clientConfig);
given()
.config(myTimeout)
.when()
.get("/delay/10")
.then()
.statusCode(200);
}
@Test
void case3(){
given()
.when()
.get("/get")
.then()
.statusCode(200);
}
}
九、文件上传测试
1、文件上传接口场景
- Content-Type 类型:multipart/form-data
import io.restassured.RestAssured;
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 TestMultiPart {
@Test
void testUploadFile(){
File myFile = new File("src/test/resources/hogwarts.txt");
RestAssured.proxy = host("localhost").withPort(8888);
RestAssured.useRelaxedHTTPSValidation();
given()
.multiPart("hogwarts", myFile)
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
}
}
import io.restassured.RestAssured;
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 TestMultiParts {
@Test
void testUploadFiles(){
File myFile = new File("src/test/resources/hogwarts.txt");
RestAssured.proxy = host("localhost").withPort(8888);
RestAssured.useRelaxedHTTPSValidation();
given()
.multiPart("hogwarts", myFile)
.multiPart("ceshiren", "{\"hogwarts\": 666}",
"application/json")
.log().headers()
.log().body()
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.statusCode(200);
}
}
十、代理配置
1、代理简介
- 介于客户端与服务器之间
- 可以监听请求和响应信息
- 充当防火墙和 Web 过滤器
2、代理前后对比 3、代理与接口测试
- 更直观的排查请求错误
- 获取正确的的接口请求与响应信息
- 开启代理工具监听请求
- 获取自动化测试的请求与响应信息
- 对比两次请求响应的区别
4、REST assured 使用代理 - 开启代理工具监听请求
- 配置代理
局部:通过 proxy() 方法 全局:定义 proxy 对象
5、使用代理请求 HTTP
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestHttpProxyConf {
@Test
void testHTTPProxy() {
RestAssured.proxy = host("localhost").withPort(8888);
given()
.proxy(8888)
.when()
.get("http://httpbin.org/get")
.then()
.log().all()
.statusCode(200);
}
}
- 使用代理请求 HTTPS
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestHttpsProxyConf {
@Test
void testHTTPProxy() {
RestAssured.proxy = host("localhost").withPort(8888);
given()
.relaxedHTTPSValidation()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.log().all()
.statusCode(200);
}
}
十、认证体系
1、认证体系简介
- 是验证通信发送者的数字身份的过程
- REST-assured 认证方案
OAuth digest certificate 证书认证 form 表单认证 basic authentication 基本认证
- 使用基本认证
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static io.restassured.specification.ProxySpecification.host;
public class TestAuth {
@Test
void testAuth(){
RestAssured.proxy = host("localhost").withPort(8888);
RestAssured.useRelaxedHTTPSValidation();
String testURL = "https://httpbin.ceshiren.com/basic-auth/hogwarts/666";
given()
.auth().basic("hogwarts", "666")
.when()
.get(testURL)
.then()
.statusCode(200);
}
}
十一,接口加密与解密
1、加密解密简介
- 加密:明文转换成密文的过程
- 解密:密文还原成明文的过程
- 常见加密算法
AES RSA MD5 Base64
2、解密方案
- 通用加密算法:使用对应解密算法
- 自研加密算法:研发提供加解密 lib
- 第三方加密服务:寻求加密方提供远程解析服务
3、接口解密实战
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
public class TestBase64 {
@Test
void testEncodeAndDecode(){
byte[] arr1 = "hogwarts".getBytes(StandardCharsets.UTF_8);
String encodedMsg = Base64.encodeBase64String(arr1);
byte[] arr2 = Base64.decodeBase64("aG9nd2FydHM=");
String decodeMsg = new String(arr2, StandardCharsets.UTF_8);
}
}
- 解密后响应断言
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import static io.restassured.RestAssured.given;
public class TestDecode {
@Test
void testDecode(){
byte[] data = "hogwarts".getBytes(StandardCharsets.UTF_8);
String secretMsg = Base64.encodeBase64String(data);
LinkedHashMap<String, String> responseForm =
given()
.formParam("msg", secretMsg)
.when()
.post("https://httpbin.ceshiren.com/post")
.then()
.log().all()
.extract().path("form");
String encodedMsg = responseForm.get("msg");
String decodedMsg = new String(Base64.decodeBase64(encodedMsg));
Assertions.assertEquals("hogwarts", decodedMsg);
}
}
十二、多套环境测试
1、多套环境测试背景
2、多套环境测试解决方案
- 测试环境 1
域名:http://httpbin.org 接口:/get - 测试环境 2
域名:https://httpbin.ceshiren.com 接口:/get
3、解决方案:
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class TestEnv {
@Test
void testOrg(){
given()
.when()
.get("http://httpbin.org/get")
.then()
.statusCode(200);
}
@Test
void testCeshiren(){
given()
.when()
.get("https://httpbin.ceshiren.com/get")
.then()
.statusCode(200);
}
}
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import static io.restassured.RestAssured.given;
public class TestEnvConf {
@BeforeAll
static void setupClass(){
HashMap<String, String> envs = new HashMap<>();
envs.put("org", "http://httpbin.org/get");
envs.put("ceshiren", "http://httpbin.ceshiren.com/get");
envs.put("default", "org");
RestAssured.baseURI = envs.get(envs.get("default"));
}
@Test
void testEnvs(){
given()
.when()
.get("/get")
.then();
}
}
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import static io.restassured.RestAssured.given;
public class TestEnvYaml {
@BeforeAll
static void setupClass() throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
File yamlFile = new File(classLoader.getResource("envs.yaml").getFile());
TypeReference<HashMap<String, String>> typeRef = new
TypeReference<HashMap<String, String>>() {};
HashMap<String, String> envs = objectMapper.readValue(yamlFile, typeRef);
RestAssured.baseURI = envs.get(envs.get("default"));
}
@Test
void testEnvs() {
given()
.when()
.get("/get")
.then()
.statusCode(200);
}
}
|