前面介绍过了rest-assured-wiki翻译,这篇我们来实战使用下

版本选用4.2.0,目前最新的是4.3.0,但有groovy版本bug。(在使用开源组件时,一定要注意最新版的风险)

使用4.3.0时遇到的bug AbstractMethodError

在使用开源组件时,遇到bug,可以第一时间去对应的issues里查看下,有时比baidu/google效率高的多

常用的链接

  • rest-assured官网
  • rest-assured-wiki
  • rest-assured-wiki翻译
  • 本篇对应的源码

1.常用语法组合

  • given()--when()--then()
  • given()--expect()--when()

given里设置参数、头、认证
when()里请求rest接口,get、post、put、delete等
expect()then()里验证结果。

given()–expect()–when()

given().param("x", "y").
expect().statusCode(400).body("lotto.lottoId", equalTo(6)).
when().get("/lotto");

given()–when()–then()

given().param("x", "y").
when().get("/lotto").
then().statusCode(400).body("lotto.lottoId", equalTo(6));

方法调用链图


看不懂这图,可以先不用急,看完下面rest-assured原生的api如何使用后,再来看这张图可能对方法间调用关系就明白了。

为了更好的演示,创建个springboot的web项目,为了简化,只有controller层和domain层,没有service和dao的逻辑。

如何创建springboot项目,就不多介绍了,不会可以参照 iworkh-springboot-helloworld

1.业务代码

1-1.交互类

JsonDataResult类主要为了将返回数据封装成统一固定格式,返回。

public class JsonDataResult<T> {protected boolean success;protected String message;protected int errorCode = 0;@JsonProperty("result")protected T data;...省略了setgeter...
}

1-2.实体类

public class UserVo {private int id;private String name;private Date birthday;private boolean vip;...省略了setgeter...
}

不多解释,一个bean

1-3.controller

UserController类里定义了增删改查接口,并@RestController注解,返回值是json

@RestController
@RequestMapping("/api/user")
public class UserController {@PostMapping("/createUserByParam")public JsonDataResult<Boolean> createUserByParam(UserVo userVo) {JsonDataResult<Boolean> result = new JsonDataResult<>();userVo.setId(new Random().nextInt(50));result.setSuccess(true);result.setData(true);return result;}@PostMapping("/createUserByJson")public JsonDataResult<Boolean> createUserByJson(@RequestBody UserVo userVo) {JsonDataResult<Boolean> result = new JsonDataResult<>();userVo.setId(new Random().nextInt(100));result.setSuccess(true);result.setData(true);return result;}@GetMapping("/{id}")public JsonDataResult<UserVo> getUser(@PathVariable int id) {JsonDataResult<UserVo> result = new JsonDataResult<>();String[] hobbies = {"football", "sing"};//从数据库查询,省略UserVo user = new UserVo(id, "iworkh" + id, System.currentTimeMillis(), true, Arrays.asList(hobbies));result.setSuccess(true);result.setData(user);return result;}@PutMappingpublic JsonDataResult<UserVo> updateUser(@RequestBody UserVo userVo) {JsonDataResult<UserVo> result = new JsonDataResult<>();//从数据库删除,省略result.setSuccess(true);result.setData(userVo);return result;}@DeleteMapping("/{id}")public JsonDataResult<Boolean> delteUser(@PathVariable int id) {JsonDataResult<Boolean> result = new JsonDataResult<>();//从数据库删除,省略result.setSuccess(true);result.setData(true);return result;}
}

2.原生rest-assured API

RestAssured类是很关键的一个类,打开发现很多方法,和默认配置,这里提下几个默认配置

  • DEFAULT_URI: 默认值是 http://localhost
  • DEFAULT_PORT: 默认值是 8080

所以,当我们端口号不是默认值时,我们也得修改配置。(比如:RestAssured.port = 9090;)

为了简单演示,一些认证都不打开了。直接演示调用rest接口测试。

引入依赖

<dependencies><dependency><groupId>io.rest-assured</groupId><artifactId>rest-assured</artifactId><version>${rest-assured.version}</version><scope>test</scope><exclusions><exclusion><artifactId>json-path</artifactId><groupId>io.rest-assured</groupId></exclusion><exclusion><artifactId>xml-path</artifactId><groupId>io.rest-assured</groupId></exclusion></exclusions></dependency><dependency><groupId>io.rest-assured</groupId><artifactId>json-path</artifactId><version>${rest-assured.version}</version><scope>test</scope></dependency><dependency><groupId>io.rest-assured</groupId><artifactId>xml-path</artifactId><version>${rest-assured.version}</version><scope>test</scope></dependency>
</dependencies>

其中rest-assured的版本是4.2.0<rest-assured.version>4.2.0</rest-assured.version>

2-1.插入

2-1-1.queryParams方式

queryParams:即通过url后面加参数方式传递

@Test
public void test001CreateUserByUrl() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));given().queryParams(BeanMapTool.beanToMap(zhangsanUser)).when().post("/api/user/createUserByParam").then().statusCode(200).body("result", equalTo(true));
}

XxxParmas方法需要的是一个Map对象,所以可以使用Map初始化值传递。
BeanMapTool.beanToMap()是个工具类,可以将bean转化为Map,使用的是org.springframework.cglib.beans.BeanMap来完成,文章最后会给出此工具类

2-1-2.params方式

queryParams:即通过post参数方式

@Test
public void test001CreateUserByParam() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));given().params(BeanMapTool.beanToMap(zhangsanUser)).when().post("/api/user/createUserByParam").then().statusCode(200).body("result", equalTo(true));
}

2-1-3.formParams方式

formParams:即通过post form表单方式

@Test
public void test001CreateUserByFormParam() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));given().formParams(BeanMapTool.beanToMap(zhangsanUser)).when().post("/api/user/createUserByParam").then().statusCode(200).body("result", equalTo(true));
}

2-1-4.json方式

contentType:指定json格式,并body传数据。(body的值,不一定json字符串,是对象也可以)

@Test
public void test003CreateUserByJson() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));given().contentType(ContentType.JSON).body(zhangsanUser).when().post("/api/user/createUserByJson").then().statusCode(200).body("result", equalTo(true));
}

2-2.更新

@Test
public void test002Update(){String[] hobbies = {"football", "play games"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));given().contentType(ContentType.JSON).body(zhangsanUser).when().put("/api/user/").then().statusCode(200).body("result.hobbies", hasItems("football", "play games"));
}

更新比较简单,使用put来更新,验证使用hasItems来验证多个结果

2-3.删除

@Test
public void test003Delete(){given().when().delete("/api/user/{id}",1).then().statusCode(200).body("result", equalTo(true));
}

删除更简单,使用delete

2-4.查询

查询留最后,因为这时我们使用最多,而且使用技巧最多的地方

2-4-1.body验证

@Test
public void test004GetUserPathParam() {given().pathParam("id", 1).when().get("/api/user/{id}").then().statusCode(200).body("result.name", equalTo("iworkh1"));
}

直接通过body的path提交值,验证

2-4-2.Response值验证

@Test
public void test004GetUserExactResponse() {Response response=given().expect().statusCode(200).when().get("/api/user/2");String name = response.path("result.name");Assert.assertThat(name, equalTo("iworkh2"));
}

直接根据Response的返回值,自己解析Response里header、body等来验证

2-4-3.转化对象

我们还可以直接将Response转化为对象,来验证处理

通过Response的as方法,参数是类型

  • 类型是普通类: Xxx.class即可
  • 类型是泛型:new TypeRef<Xxx>(){}来转化为需要的对象
@Test
public void test004GetUserToBean() {Response response = RestAssuredTool.get("/api/user/3");JsonDataResult<UserVo> userVoJsonResult = response.as(new TypeRef<JsonDataResult<UserVo>>() {});System.out.println(userVoJsonResult.getData());Assert.assertThat(userVoJsonResult.getData().getName(), equalTo("iworkh3"));
}

3.封装rest-assured API

原生rest-assured API使用起来,非常的灵活,但是对于开发者而言,一直...(Fluent风格)的方法调用比较麻烦。

我比较倾向于,调用一个方法,把需要参数都传过去,就结束了,不需要关系底层太多调用,因此对原生的API做下封装

封装好的好处

  • 认证在封装里做,不用在测试代码中去验证
  • 业务测试代码更关注业务,而不用太关注rest-assured的使用

缺点

  • 被封装后的方法,不够灵活。(不灵活,那就原生API,只要留出接口就行)

文章最后,给出封装好的RestAssuredTool类,当然这封装的不一定满足所有场合,也不是最好的。(大家可根据自己的需求来封装,这只抛砖引玉下)

调用封装后的测试类

package com.iworkh.test.restassured.controller;import com.iworkh.test.restassured.domain.vo.JsonDataResult;
import com.iworkh.test.restassured.domain.vo.UserVo;
import com.iworkh.test.restassured.utils.BeanMapTool;
import com.iworkh.test.restassured.utils.RestAssuredTool;
import io.restassured.common.mapper.TypeRef;
import io.restassured.response.Response;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;import java.util.Arrays;
import java.util.Map;import static org.hamcrest.Matchers.equalTo;/*** UserController测试类** @author: iworkh-沐雨云楼* @date: 2020-06-18*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UserControllerTest {private String userBaseUrl = "/api/user";@Testpublic void test001CreateUserByParam() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));Response resp = RestAssuredTool.postWithParams(userBaseUrl + "/createUserByParam",BeanMapTool.beanToMap(zhangsanUser));RestAssuredTool.validateStatusCode(resp, 200);RestAssuredTool.validateEqualTo(resp, "result", true);}@Testpublic void test001CreateUserByJson() {String[] hobbies = {"football", "sing"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));Response resp = RestAssuredTool.postWithJson(userBaseUrl + "/createUserByJson", zhangsanUser);RestAssuredTool.validateStatusCode(resp, 200);}@Testpublic void test002Upate() {String[] hobbies = {"football", "play games"};UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));Response response = RestAssuredTool.putWithJson(userBaseUrl, zhangsanUser);RestAssuredTool.validateStatusCode(response, 200);}@Testpublic void test003Delete() {Response response = RestAssuredTool.delete(userBaseUrl + "/id");RestAssuredTool.validateStatusCode(response, 200);}@Testpublic void test004GetUser01() {Response resp = RestAssuredTool.get(userBaseUrl + 1);RestAssuredTool.validateHasItems(resp, "result.hobbies", "football", "sing");}@Testpublic void test004GetUser02() {Response response = RestAssuredTool.get(userBaseUrl + 2);int id = response.path("result.id");Assert.assertThat(id, equalTo(2));}@Testpublic void test004GetUser03() {Response response = RestAssuredTool.get(userBaseUrl + 3);// 转化为JsonDataResult对象,不过data部分是map,再使用BeanMapTool工具可以转为对对应的对象JsonDataResult<Map<String, ?>> userVoJsonDataResult = RestAssuredTool.asJsonDataResult(response);System.out.println(userVoJsonDataResult.getData());Assert.assertThat(userVoJsonDataResult.getData().get("name"), equalTo("iworkh3"));try {UserVo userVo = BeanMapTool.mapToBean(userVoJsonDataResult.getData(), UserVo.class);Assert.assertThat(userVo.getName(), equalTo("iworkh3"));} catch (IllegalAccessException | InstantiationException e) {e.printStackTrace();}}@Testpublic void test004GetUser04() {Response response = RestAssuredTool.get(userBaseUrl + 4);// 通过泛型转化为需要的对象JsonDataResult<UserVo> userVoJsonResult = RestAssuredTool.asGeneric(response,new TypeRef<JsonDataResult<UserVo>>() {});Assert.assertThat(userVoJsonResult.getData().getName(), equalTo("iworkh4"));}
}

代码中重要的地方都有注释,就不多解释了

4.工具类

4-1.BeanMapTool

bean转map工具类

参照博客 工具类–bean和map互转

使用的是里面的BeanMapTool工具类

4-2.RestAssuredTool

  • 源码所在位置

需要扩展的几个点:

  • port和baseURI修改成从配置文件读取
  • 在初始化restClient时,将认证加上

其他如何操作可以查看官网或者查看翻译的wiki rest-assured wiki翻译

package com.iworkh.test.restassured.utils;import com.iworkh.test.restassured.domain.vo.JsonDataResult;
import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.http.ContentType;
import io.restassured.http.Headers;
import io.restassured.path.json.JsonPath;
import io.restassured.path.json.config.JsonPathConfig;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;import java.util.Map;import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;/*** RestAssuredTool工具类** @author: iworkh-沐雨云楼* @date: 2020-06-18*/
public class RestAssuredTool {static {// 这可以修改为从配置文件读取String siteBaseURI = "http://localhost";int port = 8080;RestAssured.baseURI = siteBaseURI;RestAssured.port = port;JsonPath.config = new JsonPathConfig("UTF-8");}public static RequestSpecification restClient() {// 认证等操作,都可以在这统一处理return given();}public static RequestSpecification restClientWithHeader(Headers headers) {// 认证等操作,都可以在这统一处理return given().headers(headers);}// getpublic static Response get(String url) {return restClient().get(url);}public static Response getWithParams(String url, Map<String, ?> params) {return restClient().params(params).get(url);}public static Response getWithQueryParams(String url, Map<String, ?> params) {return restClient().queryParams(params).get(url);}public static Response getWithFormParams(String url, Map<String, ?> params) {return restClient().formParams(params).get(url);}public static <T> Response getWithJson(String url, T data) {return restClient().contentType(ContentType.JSON).body(data).get(url);}// postpublic static Response postWithParams(String url, Map<String, ?> params) {return restClient().params(params).post(url);}public static Response postWithFormParams(String url, Map<String, ?> params) {return restClient().formParams(params).post(url);}public static <T> Response postWithJson(String url, T data) {return restClient().contentType(ContentType.JSON).body(data).post(url);}// putpublic static Response putWithParams(String url, Map<String, ?> params) {return restClient().params(params).put(url);}public static Response putWithFormParams(String url, Map<String, ?> params) {return restClient().formParams(params).put(url);}public static <T> Response putWithJson(String url, T data) {return restClient().contentType(ContentType.JSON).body(data).put(url);}// deletepublic static Response delete(String url) {return restClient().delete(url);}public static <T> Response deleteWithJson(String url, T data) {return restClient().contentType(ContentType.JSON).body(data).delete(url);}// validate responsepublic static void validateStatusCode(Response response, int expectedStatusCode) {response.then().statusCode(expectedStatusCode);}public static <T> void validateEqualTo(Response response, String path, T expectedValue) {response.then().body(path, equalTo(expectedValue));}public static <T> void validateHasItems(Response response, String path, T... expectedValue) {response.then().body(path, hasItems(expectedValue));}// convertpublic static JsonDataResult<Map<String, ?>> asJsonDataResult(Response response) {return response.as(new TypeRef<JsonDataResult<Map<String, ?>>>() {});}public static <T> T asGeneric(Response response, TypeRef<T> typeRef) {return response.as(typeRef);}public static <T> T asCls(Response response, Class<T> cls) {return response.as(cls);}
}

rest-assured实战相关推荐

  1. Mongodb 集群实战

    该实战过程完全跟着官网一步一步实现 ,官网教程:https://docs.mongodb.com/manual/tutorial/atlas-free-tier-setup/ 使用Mongo Shel ...

  2. FreeSWITCH+CentOS7.9+firewalld+fail2ban Internet部署的安全加固实战

    在Internet上部署sip服务器的同学都知道,各种scanner不停的探测,尝试各种呼叫规则,尝试各类国际呼叫,如果没有前置防火墙,会被烦死,笔者就亲见因sip用户密码设置太简单而被恶意盗打国际长 ...

  3. 利器 | Java 接口自动化测试首选方案:REST Assured 实践 (一)

    在 REST Assured 的官方 GitHub 上有这样一句简短的描述: Java DSL for easy testing of REST services 简约的 REST 服务测试 Java ...

  4. IDEA的Docker插件实战(Dockerfile篇)

    IDEA的Docker插件实战(Dockerfile篇) IntelliJ IDEA的Docker插件能帮助我们将当前工程制作成Docker镜像.运行在指定的远程机器上,是学习和开发阶段的好帮手,本文 ...

  5. 数据结构(04)— 线性顺序表实战

    1. 设计思路 本实战的实质是完成对学生成绩信息的建立.查找.插入.修改.删除等功能,可以首先定义项目的数据结构,然后将每个功能写成一个函数来完成对数据的操作,最后完成主函数以验证各个函数功能并得出运 ...

  6. 【置顶】利用 NLP 技术做简单数据可视化分析教程(实战)

    置顶 本人决定将过去一段时间在公司以及日常生活中关于自然语言处理的相关技术积累,将在gitbook做一个简单分享,内容应该会很丰富,希望对你有所帮助,欢迎大家支持. 内容介绍如下 你是否曾经在租房时因 ...

  7. 2 用python进行OpenCV实战之图像基本知识

    前言 在这一节,我们将学习图像的基本构成单元--像素,我们将详细的探讨什么是像素?像素是如何使用来构成图像的?然后学习如何通过OpenCV来获取和操纵像素. 1 什么是像素 所有的图像都包含一组像素, ...

  8. PyTorch 高级实战教程:基于 BI-LSTM CRF 实现命名实体识别和中文分词

    20210607 https://blog.csdn.net/u011828281/article/details/81171066 前言:译者实测 PyTorch 代码非常简洁易懂,只需要将中文分词 ...

  9. 实战清除电脑上恶意弹出广告窗口

    实战清除电脑上恶意弹出广告窗口 当你碰到电脑桌面右下角时不时弹出广告,如游戏推广.商品广告等,怎么删也删不掉,这是因为用户不小心安装有捆绑广告推广的软件,系统被静默安装了恶意木马广告,这不仅仅是影响用 ...

  10. deeplearning模型量化实战

    deeplearning模型量化实战 MegEngine 提供从训练到部署完整的量化支持,包括量化感知训练以及训练后量化,凭借"训练推理一体"的特性,MegEngine更能保证量化 ...

最新文章

  1. 将Quartz.NET集成到 Castle中
  2. 外媒评李开复的《AI·未来》:四大浪潮正在席卷全球
  3. lua学习笔记之io
  4. neural network ppt for support vector machine
  5. 亿些模板【数据结构】
  6. 工作290:js日期操作
  7. centos 使用yum 安装出现 File contains no section headers 错误解决方法
  8. python合并表格_python合并表格sheets
  9. 泰牛php第10期百度云,泰牛程序员 韩顺平 2017年 MyBatis
  10. 一步一步实现 iOS 微信自动抢红包(非越狱)
  11. DirectX9学习(四)装载位图
  12. html禁止查看图片,强看被屏蔽微信朋友圈
  13. 国内工业软件排行榜、市场份额与主要玩家
  14. 基于SSH的员工信息管理系统
  15. dva 底层框架构建
  16. CCD相机与普通相机的区别
  17. 临沂办理高新技术企业需要什么文件及材料
  18. Easy3D 孔洞识别
  19. ZoneAlarm发布新的升级版
  20. js/input/输入框 只能输入汉字/数字/英文

热门文章

  1. 任意顺序的四个点获取矩形的中心点,长宽和角度
  2. 电路基础之:差模信号与共模信号
  3. Dubbo-admin
  4. 音乐付费的大门为谁而开?
  5. 在阿里云主机上部署Mysql数据库详细教程
  6. 方正BPM开发平台(FIX ES2007)帮助手册+知识库
  7. import requests
  8. VS2013各版本序列号
  9. android 支付宝未安装,调用支付宝接口Android客户端没有支付宝APP的情况下解决无法调用支付宝页面的问题...
  10. python网易公开课官网_可汗学院公开课:计算机科学