文章目录

  • 用Rest assured作API自动化集成测试
    • 前言
    • 文档
    • 环境
    • Rest assured依赖
    • 测试示例
      • HTTP基础
      • 测试GET方法
        • 打印HTTP response
        • 测试路径参数(Path variable)
        • 测试查询参数(Query param)
        • 判断单个JSON对象
        • 判断JSON对象集合
        • 抽取HTTP response
      • 测试POST方法
        • 入参为JSON
        • 提交表单
    • 小结
    • 参考文档

用Rest assured作API自动化集成测试

前言

在用Spring Boot开发时,可以用Spring Boot Test + JUnit + Maven来做API自动化单元测试。这种测试的特点:

  • 深度依赖于Spring Boot Test测试框架。
  • 白盒测试,需要完全了解功能代码细节才能编写测试代码。
  • Mock测试,只适用于小范围的单元测试。

除吃之外,还可以使用Rest assured + JUnit + Maven做API自动化集成测试。这种测试的特点:

  • 不依赖于开发框架和语言。
  • 黑盒测试,不需要了解功能代码细节就可以写测试代码。
  • 真实测试,适用于API的端到端的集成测试。

文档

Rest assured文档:

  • Rest assured官网:https://rest-assured.io/
  • Rest assured GitHub:https://github.com/rest-assured/rest-assured/
  • Rest assured Getstarted: https://github.com/rest-assured/rest-assured/wiki/GettingStarted
  • Rest assured Usage (<= Java 1.9.0): https://github.com/rest-assured/rest-assured/wiki/Usage_Legacy
  • Rest assured Usage (> Java 1.9.0): https://github.com/rest-assured/rest-assured/wiki/Usage
  • Rest assured Maven repository: https://mvnrepository.com/artifact/io.rest-assured

环境

本文的测试环境:

  • Java8
  • JUnit4
  • Maven
  • Rest assured

Rest assured依赖

Rest assured的Maven依赖:

<dependency><groupId>io.rest-assured</groupId><artifactId>rest-assured</artifactId><version>4.3.0</version><scope>test</scope>
</dependency>

静态导入相关包:

import static io.restassured.RestAssured.*;
import static io.restassured.matcher.ResponseAwareMatcher.*;
import static org.hamcrest.Matchers.*;

测试示例

下面的测试示例,以在线模拟REST API测试网站作为要测试的对象:

  • https://jsonplaceholder.typicode.com/
  • https://reqres.in/

HTTP基础

简单回顾一下调用REST API的过程:

  1. 客户端发起HTTP请求:

    • 指定HTTP方法,比如:GET,POST

    • 指定HTTP协议,比如:HTTP/1.1

    • 指定URL,比如:https://reqres.in/api/users/2

    • 提供HTTP request报文包括HTTP request headers和HTTP request body。

  2. 服务端响应HTTP请求:

    • HTTP status code,比如200,详情参见:https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
    • HTTP status message
    • HTTP response报文包括HTTP response headers和HTTP response body。

在Postman中,打开View / Show postman console,可以查看详细的HTTP日志。

关于HTTP的更多内容,参见:

  • HTTP Overview 英文版:https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
  • HTTP Overview 中文版:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Overview

测试GET方法

打印HTTP response

打印HTTP response的全部报文或只打印response body。

@Test
public void test_get_log_all() {String url = "https://jsonplaceholder.typicode.com/posts/1";get(url).then().log().all();
}@Test
public void test_get_log_body() {String url = "https://jsonplaceholder.typicode.com/posts/1";get(url).then().log().body();
}

测试路径参数(Path variable)

传入路径参数(path variable),并判断Status code是否为200,且Content-type为JSON。

@Test
public void test_get_with_path_variable() {String url = "https://jsonplaceholder.typicode.com/posts/{id}";get(url, 1).then().statusCode(200).contentType(ContentType.JSON);
}

测试查询参数(Query param)

传入查询参数(query param),并判断Status code是否为200,且Content-type为JSON。

@Test
public void test_get_with_query_param() {String url = "https://jsonplaceholder.typicode.com/comments";given().param("postId", 1).get(url).then().statusCode(200).contentType(ContentType.JSON);
}

判断单个JSON对象

以https://reqres.in/api/users/2为例,返回的JSON内容类似:

{"data": {"id": 2,"email": "janet.weaver@reqres.in","first_name": "Janet","last_name": "Weaver","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"},"ad": {"company": "StatusCode Weekly","url": "http://statuscode.org/","text": "A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."}
}

验证上面JSON的data部分:

@Test
public void test_validate_json() {String url = "https://reqres.in/api/users/2";get(url).then().statusCode(200).contentType(ContentType.JSON).body("data.id", equalTo(2)).body("data.email", equalTo("janet.weaver@reqres.in")).body("data.first_name", equalTo("Janet")).body("data.last_name", equalTo("Weaver")).body("data.avatar", not(emptyOrNullString()));
}

Rest assured支持通过JSON Path (比如data.email )来判断返回的JSON内容的字段的值。

判断JSON对象集合

以https://reqres.in/api/users?page=2为例,返回的JSON内容类似:

{"page": 2,"per_page": 6,"total": 12,"total_pages": 2,"data": [{"id": 7,"email": "michael.lawson@reqres.in","first_name": "Michael","last_name": "Lawson","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg"},{"id": 8,"email": "lindsay.ferguson@reqres.in","first_name": "Lindsay","last_name": "Ferguson","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/araa3185/128.jpg"},{"id": 9,"email": "tobias.funke@reqres.in","first_name": "Tobias","last_name": "Funke","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/vivekprvr/128.jpg"},{"id": 10,"email": "byron.fields@reqres.in","first_name": "Byron","last_name": "Fields","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/russoedu/128.jpg"},{"id": 11,"email": "george.edwards@reqres.in","first_name": "George","last_name": "Edwards","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/mrmoiree/128.jpg"},{"id": 12,"email": "rachel.howell@reqres.in","first_name": "Rachel","last_name": "Howell","avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/hebertialmeida/128.jpg"}],"ad": {"company": "StatusCode Weekly","url": "http://statuscode.org/","text": "A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."}
}

可以看到data部分是一个数组。

@Test
public void test_validate_json_array() {String url = "https://reqres.in/api/users?page=2";get(url).then().statusCode(200).contentType(ContentType.JSON).body("data.id", hasItems(7, 8, 9, 10, 11, 12)).body("data.email", hasItems(endsWith("@reqres.in"))).body("data.first_name", hasItems("Michael", "Lindsay", "Tobias", "Byron", "George", "Rachel")).body("data.last_name", hasItems("Lawson", "Ferguson", "Funke", "Fields", "Edwards", "Howell")).body("data.avatar", hasItems(not(emptyOrNullString())));
}

抽取HTTP response

可以将HTTP response抽取到一个变量、Map和List:

@Test
public void test_extract_response() {String url = "https://reqres.in/api/users/2";Response response = get(url).then().extract().response();System.out.println(response.getStatusCode());System.out.println(response.getContentType());System.out.println(response.asString());response.getBody().prettyPrint();// 抽取某个字段的值System.out.println((String)response.path("data.email"));// 抽取到Map中Map map = (HashMap) response.path("data");System.out.println(map);
}@Test
public void test_extract_response_list() {String url = "https://reqres.in/api/users?page=2";Response response = get(url).then().extract().response();// 抽取对象数组到List中List list = (ArrayList) response.path("data");System.out.println(list);
}

也可以抽取到类的对象中。

在测试类中,自定义类:

@Data
static class Result {private ResultData data;@JsonIgnoreprivate Object ad;
}@Data
static class ResultData {private Long id;private String email;@JsonProperty("first_name")private String firstName;@JsonProperty("last_name")private String lastName;private String avatar;
}

说明:

  • @Data为Lombok注解,参见:https://blog.csdn.net/nklinsirui/article/details/104501340

  • 需要引入jackson-databind依赖,来完成JSON序列化和反序列化,比如:

    <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.9</version>
    </dependency>
    

测试抽取到类的对象:

@Test
public void test_extract_response_to_class_object() {String url = "https://reqres.in/api/users/2";Response response = get(url).then().extract().response();// 抽取到类的对象中Result result = response.as(Result.class);System.out.println(result);
}

通过jsonpath来抽取集合:

@Test
public void test_extract_response_to_class_object_list() {String url = "https://reqres.in/api/users?page=2";Response response = get(url).then().extract().response();List<ResultData> resultDataList = response.jsonPath().getList("data");System.out.println(resultDataList);List<String> emailList = response.jsonPath().getList("data.email");System.out.println(emailList);
}

测试POST方法

入参为JSON

URL:https://reqres.in/api/register

HTTP方法:POST

数据格式:JSON

入参:

{"email": "eve.holt@reqres.in","password": "pistol"
}

出参:

{"id": 4,"token": "QpwL5tke4Pnpja7X4"
}

测试代码:

@Data
static class User {private String email;private String password;
}@Test
public void test_post_json_pojo() {User user = new User();user.setEmail("eve.holt@reqres.in");user.setPassword("pistol");String url = "https://reqres.in/api/register";given().contentType(ContentType.JSON).body(user).when().post(url).then().statusCode(200).contentType(ContentType.JSON).body("id", greaterThan(0)).body("token", not(emptyOrNullString()));
}@Test
public void test_post_json_hash_map() {Map<String, Object> userMap = new HashMap<>();userMap.put("email", "eve.holt@reqres.in");userMap.put("password", "pistol");String url = "https://reqres.in/api/register";given().contentType(ContentType.JSON).body(userMap).when().post(url).then().statusCode(200).contentType(ContentType.JSON).body("id", greaterThan(0)).body("token", not(emptyOrNullString()));
}

说明:

  • 可以定义一个和入参结构一样的User类,传入User类的对象作为request body。
  • 也可以传入HashMap作为request body。
  • 也可以传入JSON字符串作为request body。

对应Postman测试:

  • HTTP方法为POST。
  • Request body中勾选“raw”,再选择“JSON"格式。

提交表单

@Test
public void test_post_form() {String url = "";given().urlEncodingEnabled(true).param("param1", "xxx").param("param2", "abc").header("Accept", ContentType.JSON.getAcceptHeader()).when().post(url).then().statusCode(0);
}

对应Postman测试:

  • HTTP方法为POST。
  • Request body中勾选“x-www-form-urlencoded”,再输入参数的Key和Value。(注意,这里不要勾选“form-data",因为这里的form-data实际上是multipart/form-data,是用于文件分段上传的)

小结

本文描述了如何用Rest assured来测试REST API的GET/PUT方法。

对测试时需要指定HTTP headers的情况,还有测试Spring Boot应用的场景,将在后续其他文章中再说明。

参考文档

  • https://www.guru99.com/rest-assured.html

  • https://techbeacon.com/app-dev-testing/how-perform-api-testing-rest-assured

  • REST-Assured,接口自动化的 “瑞士军刀”- 初识篇

  • Hamcrest

  • https://stackoverflow.com/questions/56642603/rest-assured-isnt-there-a-better-way-to-test-an-array-of-results

  • https://devqa.io/rest-assured-post-request/

  • https://techeplanet.com/rest-assured-post-json/

  • postman中 form-data、x-www-form-urlencoded、raw、binary的区别

用Rest assured作API自动化集成测试相关推荐

  1. 浅谈软件自动化集成测试的流程

    浅谈自动化集成测试 相信从事软件测试专业的同行很早就知道了自动化的测试技术,也许大家也很想知道具体的软件自动化具体的运行实施过程.本人学识尚欠,目前无法对综合的软件自动化的测试进行阐述,但是本人通过不 ...

  2. python 自动化-Python API 自动化实战详解(纯代码)

    主要讲如何在公司利用Python 搞API自动化. 1.分层设计思路 dataPool :数据池层,里面有我们需要的各种数据,包括一些公共数据等 config :基础配置 tools : 工具层 co ...

  3. api自动化_如何在不增加人员的情况下自动化API安全程序

    api自动化 在这篇文章中,我们将撰写一篇综合文章,内容涉及如何在不增加人员的情况下自动执行API安全程序. 在现代世界中,数据对于提供者和消费者都至关重要. 数据科学的出现证明了这一事实. 对于某些 ...

  4. e2e 自动化集成测试 架构 实例 WebStorm Node.js Mocha WebDriverIO Selenium Step by step (四) Q 反回调...

    上一篇文章"e2e 自动化集成测试 架构 京东 商品搜索 实例 WebStorm Node.js Mocha WebDriverIO Selenium Step by step (三) Sq ...

  5. mocha 测试 mysql_e2e 自动化集成测试 架构 实例 WebStorm Node.js Mocha WebDrive

    e2e 自动化集成测试 架构 京东 商品搜索 实例 WebStorm Node.js Mocha WebDriverIO Selenium Step by step 二 图片验证码的识别 , 下面讲一 ...

  6. Api自动化框架分享

    前言 嗨咯!兄弟们!乡亲们!好久没有发帖了!今天把我最近的一点小小的收获分享给到大家,希望能给大家一点帮助和启发-当然最需要的还是大家的意见! 今天给大家分享的是最近开发的一个API自动化框架: 可能 ...

  7. 利用openVuln API自动化查询思科产品漏洞

    利用openVuln API自动化查询思科产品漏洞 B站视频链接:​​https://www.bilibili.com/video/BV1ZG411P7cf/?spm_id_from=333.999. ...

  8. 自动化运维(使用api自动化管理f5设备)

    自动化运维(使用api自动化管理f5设备) 前言 F5 的API接口 关于认证 利用python实现自动化巡检 成果 附录(f5 api reference) 前言 利用F5设备自带的API,通过py ...

  9. 防微杜渐,未雨绸缪,百度网盘(百度云盘)接口API自动化备份上传以及开源发布,基于Golang1.18

    奉行长期主义的开发者都有一个共识:对于服务器来说,数据备份非常重要,因为服务器上的数据通常是无价的,如果丢失了这些数据,可能会导致严重的后果,伴随云时代的发展,备份技术也让千行百业看到了其" ...

最新文章

  1. python学生管理系统-学生管理系统python
  2. Redis-cluster架构
  3. java string转number_Java运算符知识点总结
  4. 三家逐鹿,私有化部署能帮神策数据杀出重围么?| 公司调研
  5. sql去除空值_SQL汇总分析
  6. 钟表的用途和作用_液体三氯化铁用途及使用方法
  7. Java中List详解
  8. 加入域--深入理解DNS在域中作用
  9. 线程名称的获取与修改
  10. JAVA-java内存分配
  11. WCF Transaction
  12. headless webkit(无界面浏览器、爬虫)
  13. 数据库 事务提交和回滚
  14. 获取汉字首字母,拼音,可实现拼音字母搜索----npm js-pinyin
  15. M1 版 MacBook SSD 为何会损耗巨大?
  16. android SDK 开发心得笔记
  17. python dict遍历_Python专题——详解enumerate与zip用法
  18. IAR编译器问题的总结
  19. 02333软件工程_202008_试卷+答案
  20. LM小型可编程控制器软件(基于CoDeSys)笔记二十七:温度电阻通道和DO通道

热门文章

  1. Linux 定时发送邮箱
  2. 【MATLAB统计分析与应用100例】案例011:matlab读取Excel数据,调用regress函数作一元线性回归分析
  3. 2022国赛论文及可运行代码
  4. 机器学习基石06:泛化理论(Theory of Generalization)
  5. 信号的宽带和计算机网络的宽带有什么不同,路由器和调制解调器(宽带猫)有什么区别?...
  6. pppoe服务器账号和密码是什么,路由器的PPPOE拨号宽带账号和密码是多少?
  7. 买礼物(线段树+set维护)
  8. 有中国电信手机一定要看。CTWAP和CTNET是什么意思?有什么区别?
  9. 【C语言】定义一个函数,求长方体的体积
  10. GD32F303固件库开发(7)----printf打印配置