目录
解析 JSON 字符串为 JsonNode 对象,进行断言编辑
使用TestNG进行参数化--@DataProvider 参数化注解
一、接口定义
接口(Interface)是一种约定俗成的规范。可以简单的理解就是一个地址,通过地址能访问到数据。能够进行前后台数据交互。
二、接口组成
接口类型包括两种:
1、程序内部的接口(自己开发写的接口)
2、系统对外的接口(引用第三方的接口)
为什么要做接口测试?
答:1) 越底层发现bug,它的修复成本是越低的 --前端(页面/数据) 调用接口(需要时间的) 后端(接口)
2) 前端随便变,接口测好了,后端不用变。 --提高测试效率
3)检查系统的安全性、稳定性,前端传参不可信。支付-前端 (金额<0) 直接跳过页面 直接通过传参的方式解决
4)项目-复杂度(业务需求越来越多),传统的测试方法成本急剧增加且测试效率大幅下降,接口测试可以提供这种情况下的解决方案。
5)工具-自动化。接口工具+持续集成工具=自动化 --效率、易用性
接口测试相对容易实现自动化持续集成,且相对UI自动化也比较稳定,可以减少人工回归测试人力成本与时间,缩短测试周期
Http协议数据见此篇文章:Http协议详解
如何进行接口测试?(针对HTTP协议较多)
工具:postman、jmeter、apipost、apifox等
代码: 一般来讲通过代码:java+httpclient、python+requests等,框架和UI自动化差不多,发送请求用断言来判断
三、接口自动化库
在 Java 接口自动化测试中,最常用的库:
1、RestAssured
2、HttpClient
3、OkHttp
3.1 RestAssured
优点:
- 测试代码简洁明了:可以编写简单、易于理解的测试代码,避免了手动发送请求和处理响应的繁琐步骤。
- 支持多种数据格式:支持多种数据格式(如SON、XML等),方便进行数据交互测试。
- 内置断言功能:内置了许多断言方法,可以方便地对响应进行校验,并输出清晰的错误信息
- 可扩展性强:可以通过插件来扩展功能,比如可以添加lackson或Gson插件来支持更多的数据转换格式
缺点:
- 有一定学习成本:语法和使用方式与传统java测试框架(如JUnit、TestNG)有些不同,需要一定学习成本
- 对于复杂场景可能不够灵活:虽然Rest-Assured支持多种数据格式和断言方法,但在处理特殊场景时可能需要编写更多的自定义代码。
示例代码:
public class RestAssurehttpPostTest {
public static void main(String[] args) throws IOException {
Map<String, String> requestParams = new HashMap<>();
requestParams.put("username", "15096264111");
requestParams.put("password", "123456");
requestParams.put("nickname", "小白");
requestParams.put("sex", "男");
requestParams.put("img",
"http://www.baidu.com/static/img/avatar.jpg");
requestParams.put("company", "null");
requestParams.put("money", "1000");
given().contentType("application/x-www-form-urlencoded;charset=UTF-8")
.formParams(requestParams)
.when()
.post("http://www.baidu.com/v1/user/manager/customer")
.then()
.statusCode(200)
.body("msg",equalTo("success"));
}
}
3.2 HttpClient
优点:
- 简单易用:HttpClient的API简单易用,使得在Java接口自动化中使用非常方便。
- 支持多种协议:HttpClient支持多种协议,例如HTTP、HTTPS等,适用于不同类型的接口测试。
- 可以设置超时时间:HttpClient允许你设置超时时间,可以控制接口请求的响应时间。
- 支持cookies管理:HttpClient支持cookies管理,可以模拟用户登录后的状态进行接口测试。
缺点:
- 不支持自动化参数化:HttpClient并不支持自动化参数化,需要自己编写代码实现参数化。
- 对URL编码要求高:HttpClient对URL编码要求相对较高,因此需要开发人员了解相关知识才能正确使用。
- 需要手动解析JSON/XML格式:HttpClient可以获取接口返回数据,但需要手动解析JSON/XML格式。
- 不支持图形界面:HttpClient只能通过编写代码来进行接口测试,不支持图形化界面,对初学者来说可能会有一定的难度。
3.3 OkHttp
优点:
- OkHttp客户端易于使用,具有良好的文档支持。
- 它支持多种协议,如HTTP/1.1、HTTP/2和WebSocket等,可满足不同场景下的需要。
- 它提供了异步请求和响应,可以提高性能和效率。
- 发送和接收数据时,OkHttpClient提供了丰富的API,可以自定义请求头、响应头及请求体等。
- 通过添加拦截器,可以方便地对请求和响应做一些额外的处理,比如记录日志、添加认证等功能。
缺点:
- 对于一些较为复杂的接口场景,需要编写更多的代码来实现功能。
- 在处理大量并发请求时,需要谨慎设计线程池和连接池参数,否则可能会影响系统性能。
示例代码:
public class okhttpPostTest {
public static void main(string[] args)throws IOException
OkHttpClient client =new OkHttpClient();
// 构造请求参数
RequestBody requestBody=new FormBody.Builder("15096264111")
.add("username","1255423")
.add("password","12345")
.add("name","小白")
.add("sex","女")
.add("img","http://www.baidu.com/static/img/avatar.jpg")
.add("company","null")
.add("money","1000")
.build();
// 构造请求
Request request=new Request.Builder()
.url("http://www.baidu.com/v1/user/manager/customer")
.post(requestBody)
.build();
//发送请求,并获取响应
Response response = client.newCall(request).execute();
String result = response.body().string();
System.out.println(result);
// 解析 JSON 响应
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(result);
String msg = jsonNode.get("msg").asText();
System.out.println(msg);
// 断言响应结果
if (msg.contains("success")) {
System.out.println("测试成功");
} else {
System.out.println("测试失败");
}
// 关闭响应和 HttpClient
response.close();
client.clone();
}
}
四、OkHttpClient语法格式
导入依赖包:
<!--OkHttpClient需要添加以下依赖-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
基本语法步骤:
OkHttp发送Get请求
package com.xuzhongfa.api;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
// 发送get请求
public class GetDemo {
public static void main(String[] args) throws IOException {
// 创建发送请求对象
OkHttpClient client = new OkHttpClient();
// 构造请求参数:get 格式为:url+路径?参数1=值1&参数2=值2
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://mbd.baidu.com/newspage/data/landingsuper") // url+路径
.newBuilder()
.addQueryParameter("offer","0") // 参数1、值1
.addQueryParameter("page","90") // 参数2、值2
.addQueryParameter("token","12343"); // 参数3、值3
// 创建请求数据 -request
Request request = new Request.Builder()
.url(urlBuilder.build()) // 注意此处 + .build()
.build();
// 发送请求
Response response = client.newCall(request).execute();
// 打印响应数据
if(response.isSuccessful()){
System.out.println("返回结果是: "+ response.body().string()); // 注意此处不是toString()
}
// 关闭请求和httpClient
response.close();
client.clone;
}
}
运行结果如下:
OkHttp发送Post请求
Post请求参数在body中,请求类型有如下方式:
Conetent-type 与 body 对应关系表
(1)Form表单形式
package com.xuzhongfa.api;
import okhttp3.*;
import java.io.IOException;
// 发送post请求-表单
public class PostDemo {
public static void main(String[] args) throws IOException {
// 创建发送请求对象
OkHttpClient client = new OkHttpClient();
// 创建 post 请求参数body
RequestBody requestBody = new FormBody.Builder()
.add("phone","hami")
.add("password","123456")
.build();
//创建请求数据 -request
Request request = new Request.Builder()
.url("http://logistics.hctestedu.com/v1/user/customer/login")
.post(requestBody) // 添加post请求的数据
.build();
// 发送请求数据
Response response = client.newCall(request).execute();
// 打印响应数据
if(response.isSuccessful()){
System.out.println("响应数据是: " + response.body().string());
}
// 关闭请求 和 httpClient
response.close();
client.clone();
}
}
Postman 接口截图如下:
(2)Json请求方式
1、将请求体中的参数以 Json 字符串的形式进行传递,使用的是 OkHttp 中的 RequestBody 类来构造请求体。首先,需要 将Json字符串解析为MediaType常量类型,即"application/json";然后使用 RequestBody.create()方法将其转换为请求体。2、注意:使用Json 格式提交方式时,需要服务器端能够正确解析 Json 格式的请求体,并按照要求进行处理。同时,如果目标api 要求请求头部中携带某些特定信息,则需要在 Request.Builder 中添加相应的请求头字段。
package com.xuzhongfa.api;
import okhttp3.*;
import java.io.IOException;
// 通过Post来发送 Json 数据
public class PostJson {
public static void main(String[] args) throws IOException {
// 创建 发送请求对象
OkHttpClient client = new OkHttpClient();
// 创建 post 请求参数body
String data = "{\"goods_id\":11,\"stock\":200}"; // Json数据
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"),data); // (数据格式,数据)
//创建请求数据 -request -url
Request request = new Request.Builder()
.url("http://shop-xo.hctestedu.com/index.php?s=/api/cart/save&application=app&application_client_type=weixin&token=e7d00b1934ef2db560071aba07d47b4d")
.post(requestBody) // 添加post请求的数据
.build();
// 发送请求数据
Response response = client.newCall(request).execute();
// 打印响应数据
if(response.isSuccessful()){
System.out.println("响应数据是: " + response.body().string());
}
// 关闭请求 和 httpClient
response.close();
client.clone();
}
}
此处Json数据格式与form表单不一致写法,注意区别
(3)文件上传形式
package com.xuzhongfa.api;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
// 通过Post来上传文件 -form-data 表单形式上传文件
public class PostFile {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
// 构造请求参数 -文件
File filepath = new File("E:\\333.jpg");
// 文件格式
MediaType mediaType = MediaType.parse("image/jpeg");
// 创建参数Body
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
//.addFormDataPart("表单数据项名称","文件名",RequestBody.create(文件地址,文件格式))
.addFormDataPart("file","upFile",RequestBody.create(filepath,mediaType))
.build();
//创建请求参数request -url
Request request = new Request.Builder()
.url("http://httpbin.org/post")
.post(requestBody)
.build();
// 发送请求数据
Response response = client.newCall(request).execute();
// 打印响应数据
if(response.isSuccessful()){
System.out.println("响应数据是: " + response.body().string());
}
// 关闭请求 和 httpClient
response.close();
client.clone();
}
}
总结:Post请求的形式不同,参数body也会不同
OkHttp发送delete请求
package com.xuzhongfa.api;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
// 发送 删除请求方法
public class DeleteDemo {
public static void main(String[] args) throws IOException {
// 创建请求对象
OkHttpClient client = new OkHttpClient();
// 请求数据 -request-url
Request request = new Request.Builder()
.url("http://logistics.hctestedu.com/v1/fleet/driver/589")
.delete()
.build();
// 发送请求
Response response = client.newCall(request).execute();
// 打印响应数据
if (response.isSuccessful()){
String str = response.body().string();
System.out.println("返回结果是: "+ str);
}
// 关闭请求 和 httpClient
response.close();
client.clone();
}
}
OkHttp发送put请求
在Http协议中,put请求方法指用来对数据body进行修改操作。
使用form表单的方式来进行修改body数据
package com.xuzhongfa.api;
import okhttp3.*;
import java.io.IOException;
// put 请求方法
public class PutDemo {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
// 修改body参数字段,建议包含全部的字段
RequestBody requestBody = new FormBody.Builder()
.add("id","171")
.add("name","小红")
.add("sex","女")
.add("phone","1589457")
.add("password","123458")
.add("bank_addr","null")
.add("bank_number","null")
.add("is_sms","false")
.add("idcard","null")
.add("email","null")
.add("remark","null")
.add("driver_license_visible","false")
.add("token","122922e1a89304e4")
.build();
//创建请求数据 -request -url
Request request = new Request.Builder()
.url("http://logistics.hctestedu.com/v1/fleet/driver/171")
.put(requestBody)
.build();
Response response = client.newCall(request).execute();
// 打印响应内容
if(response.isSuccessful()){
String s = response.body().string();
System.out.println("响应内容为:"+s);
}
// 关闭请求 和 httpClient
response.close();
client.clone();
}
}
五、断言
在lava接口自动化中,使用OkHttp发送HTTP请求后,可以通过获取响应的状态码、响应头信息以及响应体数据来进行断言。
一般情况下,我们会先将响应体数据转换成字符串或Json对象,然后对其中的某些字段或属性进行断言。
Json提取
在Java中,对于Json数据的提取有2种方式。
1、可以使用JSON库(如Jackson)来解析这个JSON数据,并提取所需数据。(解析 Json 字符串为 JsonNode 对象)
2、使用JsonPath 方式来获取Json数据 (解析 Json 字符串为 JsonPath 对象)
以解析 Json 字符串为 JsonNode对象为例说明
导入Jackson依赖
<!--添加ObjectMapper依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.5</version>
</dependency>
准备Json数据
{
"name":"小红",
"age":"16",
"msg":{
"sex":"女",
"hobbies":["游泳","跳舞","唱歌"]
}
}
解析Json数据来得到name和hobbies信息
package com.xuzhongfa.api;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonDemo {
public static void main(String[] args) throws JsonProcessingException {
String jsonData = "{\n" +
" \"name\":\"小红\",\n" +
" \"age\":\"16\",\n" +
" \"msg\":{\n" +
" \"sex\":\"女\",\n" +
" \"hobbies\":[\"游泳\",\"跳舞\",\"唱歌\"]\n" +
" }\n" +
"}";
// 创建获取json数据的objectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 解析 Json 数据
JsonNode jsonNode = objectMapper.readTree(jsonData);
// 获取年龄和爱好
String age = jsonNode.get("age").asText(); // asText() 为转化为文本
System.out.println("年龄为:" + age);
JsonNode hobbit = jsonNode.get("msg").get("hobbies"); // 两层需要依次获取
for(JsonNode s : hobbit){
System.out.println("爱好为:" + s.asText());
}
}
}
TestNG断言
断言其实就是将返回的响应数据body(格式为Json)读取,然后进行需要验证的信息进行判断
导入TestNG和Jsonpath依赖
<!-- TestNG依赖 -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
<!-- Jsonpath依赖 -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
</dependency>
<!--添加ObjectMapper依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.5</version>
</dependency>
解析 JSON 字符串为 JsonNode 对象,进行断言
解析 JSON 字符串为 JsonPath 对象,进行断言
推荐使用JsonPath去读取Json数据
- 使用 JsonPath 进行查询和验证会更方便,而在需要复杂操作时再结合 JsonNode。
- 如果接口测试主要集中在提取和验证 JSON 数据的特定部分,使用 JsonPath 对象 会更为高效和简洁。如果需要对 JSON 数据进行复杂的操作(如修改、添加或删除节点),则使用 JsonNode对象 更为合适。
JsonPath的学习链接见此篇文章:JsonPath 详解
使用TestNG进行参数化--@DataProvider 参数化注解
详情见此篇文章:https://blog.csdn.net/qq_73471456/article/details/141096997
六、Cookie 、Session、Token的概念
cookie与seesion的区别见此文章:cookie与session区别
cookie、session、token的详细解答见下篇文章:cookjie\session\token 详解
Token定义
- APIToken(接口令牌):用于访问不需要登录的接口
- USER Token(用户令牌):用于访问需要用户登录之后的接口
- Token的实效性:token可以是一次性的、也可以在一段时间范围内是有效的
- Token一般放在请求头headers或者body参数里面。一般在接口文档会有说明
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
七、接口测试用例编写原则
1、功能测试
①接口测试的功能测试就是要保证这个接口功能是可以使用的,因此按照接口文档所要求的参数,进行正常传入,判断是否可以返回正确的结果。
②考虑参数组合
现在有一个操作商品的接口,有个字段type,传1的时候代表修改商品,商品id、商品名称、价格有一个是必传的。type传2的时候是删除商品,商品id是必传的。
这样的,就要测参数组合了,type传1的时候,只传商品名称能不能修改成功,只传商品名称和商品价格能不能修改成功,id、名称、价格都传的时候能不能修改成功。
2、异常性测试
异常测试场景,也就是我不按照你接口文档上的要求输入参数,来验证接口对异常情况的校验。比如说必填的参数不填,必须输入整数类型的,我就非要传入字符串类型,长度要求是10位的,传11。
总之就是你说怎么来,我就不怎么来,其实也就这三种:必传,参数类型,参数长度
3、安全性测试
①绕过参数验证
购物网站商品价格是100元,进行接口测试,提交订单时,修改订单价格参数为20元,后端是否进行了数据验证,修改成价格为-3元,我的余额是否进行了增加
②绕过身份验证
购物网站的商品价格和库存数量只有特定超级管理员进行修改,那我传一个普通用户是否可以进行修改,传一个其他超级管理员也是否可以修改
③参数加密验证
1、用户在进行登录注册界面时,输入的用户名和密码是否进行了参数加密 来存储到数据库中
2、密码安全规则,对密码的复杂程度进行效验
④SQL注入风险、病毒文件验证
1、验证接口中参数传递时,SQL注入风险问题验证
2、验证上传文件时的病毒文件安全验证
4、兼容性测试
①验证 新版本的接口调用旧版本的接口是否可以进行调用成功
八、结合 Allure 快速生成测试报告
导入Allure依赖
<!--testng测试报告-->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>2.14.0</version>
<scope>compile</scope>
</dependency>
使用Allure
allure serve ./allure-results
详情使用见此篇文章:https://blog.csdn.net/qq_73471456/article/details/141096997
详细见本地笔记 下载资料