一个基于多接口的业务自动化测试框架!

news2025/2/3 6:53:22

这是一个成熟的框架,不是要让别人当小白鼠,它已经先后在两家公司的5条业务线进行了推广应用,用例条数到了几千条以上,并且从2018年开始每天都在CI/CD流程中被调用执行。

已有那么多接口测试框架,为什么重复造轮子?首先,本框架如题目描述,适用于多接口的业务自动化测试,不是简单的接口测试框架;其次框架始于2017、2018 年,当时也没有现在如此多的接口测试框架。

代码地址:https://github.com/HzlGauss/bulls

框架介绍

接口自动化测试无疑是测试提效最为行之有效的方案,市面上的接口自动化测试框架很众多,而本框架与其它框架的区别如以下:

  • 用例代码编写简单,让使用者精力集中在所测试系统的业务逻辑上,而 http 接口的定义,请求的发送,测试报告信息等都由框架完成;

  • 不只适用于单个接口的测试,同样适用于多个接口组成的完整的业务逻辑的测试,这往往是接口自动化测试更应该做到的;

  • 登录等前置的业务操作也由框架完成,用例中只需引用相应 cookie;

  • 框架同样支持环境、各类账号以及其它测试物料信息维护;

  • 简单易用,java 小白也能在半小时内学会使用。

框架结构

图片

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:691998057【暗号:csdn999】

上手指南

工程结构说明

图片

下面是一个论坛登录、浏览帖子、帖子点赞这样一个简单的业务场景进行举例,如何用框架完成这一几步操作的。

定义http接口

接口定义是在 yml 文件中,建议按照被测系统维护 yml 文件

api:
  globalVariables:
    - UA: "Mozilla/5.0(Macintosh;IntelMacOSX10_13_6)AppleWebKit/537.36(KHTML,likeGecko)Chrome/81.0.4044.138Safari/537.36"
  pioneers:
    - name: testerhome登录
      id: testerhomeLogin
      priority: 1
      path: https://$testerhomeHost/account/sign_in
      method: post
      headers: >-
        \{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","User-Agent":"$UA","x-requested-with":"XMLHttpRequest",
        "cookie":"user_id=eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1UVTJOamM9IiwiZXhwIjpudWxsLCJwdXIiOiJjb29raWUudXNlcl9pZCJ9fQ%3D%3D--43f5d4f117b5459e67c85cc6c569820abb1e6068; _homeland_session=Y2ljEAtdhRcbEHaTSSHMb3%2FUyn0aLrFrHoEP8QVjVq%2BvXMCEi9n57WDgHBw40L%2Bo%2Fghe148%2B%2B429DbYDWNAiC4FBFYFnEghtzkQWPpKsOm21DZQkUDLvYqr4Z2ylpkiGHqjpppkhw0LLke61psEh7ZKQte3Ia3TTzTSu9ifDtHEl9FBlZUXNgwi%2F5kscioZqkobTyJpCGp5M4mSrLiunIZUHbgm05AuWa5%2Bu2TwgsxOfpdAumg6Q0SoT7ipMLaGaprobuP0Kj2q5ZH4CKqG7fb%2FU0WwzsTgTCtMXaWLz5WYHizGKRD5CWysSMseGn5I%3D--5LouY27EpiVkGarr--tpTXhgdFShw4Qyn6sThkpg%3D%3D",
        "x-csrf-token":"zr6fgSyPS5nyqcwGdzD7R6T51aAK6L9Dv42Lao0CSPZo4jEn3pT5fNN2eTk84VdmqhzQasF+sdHQrvvxsLYSmg=="\}
      parameters: user[login]=&user[password]=&user[remember_me]=0&user[remember_me]=1&commit=登录
      extractors: \[{"name":"token","value":"cookies"}\]
  requests:
    - name: 读帖子
      id:  topics
      path: https://$testerhomeHost/topics/38484
      method: get
      headers: >-
        \{"User-Agent":"$UA","Content-Type":"application/x-www-form-urlencoded","cookie":"$token","x-requested-with":"XMLHttpRequest","x-csrf-token":"r3E8899sEAEnqST2dmtIEluqG5C/nL/Rwp2l4ITtNDU3XpF4eULhClMRoWweMt6XWSmBn2H8fmPRas+CVkA/BA=="\}
    - name: 点赞
      id:  likes
      path: https://$testerhomeHost/likes
      method: post
      headers: >-
        \{"User-Agent":"$UA","Content-Type":"application/x-www-form-urlencoded","cookie":"$token","x-requested-with":"XMLHttpRequest","x-csrf-token":"r3E8899sEAEnqST2dmtIEluqG5C/nL/Rwp2l4ITtNDU3XpF4eULhClMRoWweMt6XWSmBn2H8fmPRas+CVkA/BA=="\}
      parameters: type=Topic&id=38484

如上,接口定义文件大体分为三部分:globalVariables,pioneers,requests。

  • globalVariables:定义全局变量,为key、value形式

  • pioneers 定义前置接口,用于定义登录等前置接口。程序启动后、用例开始执行前,会自动先执行pioneers中定义的接口。其中name随意起;id 要唯一,建议按照接口请求地址的缩写命名id属性;priority,整数类型,当pioneers中定义了多个接口,执行时会按照priority属性排序,之后顺序执行。extractors:接口返回内容的提取,name,为提取的变量命名,后面接口可以通过 $name 名对其进行引用;value,变量的提取内容,支持提取cookie或返回json字符串中的某个属性 (填写属性的json path)

  • requests定义接口,基本同pioneers部分,少了extractors部分。

说明:此处的接口请求参数可以通过抓包工具抓包获取,然后复制到这里。接口定义只需定义一次,在用例中随意获取,使用接口时,根据需要设置请求参数,未设置的请求参数按照此处定义的值作为默认值。

用例代码:

@Test(enabled = true, description = "打开帖子详情页→点赞")public void test() {
    log.info("test start");
    //请求实例1,打开帖子详情页
    Request request = Request.getInstance("topics");
    //请求1发送
    Response response = request.doRequest();
    //返回为html,取其中的x_csrf_token,后面点赞接口用
    String html = response.asString();
    Headers  headers = response.getHeaders();
    Map<String, String> cookies = response.getCookies();
    Document document = Jsoup.parse(html);
    Element metaElement = document.select("meta[name=csrf-token]").first();
    String x_csrf_token = null;
    if (metaElement != null) {
        x_csrf_token = metaElement.attr("content");
    }
    //请求实例2,点赞接口
    request = Request.getInstance("likes");
    //更新cookie
    request.addCookies(cookies);
    if (x_csrf_token != null) {
        request.addHeader("x-csrf-token",x_csrf_token);
    }
    //发送点赞请求
    response = request.doRequest();
    assertThat(response.getStatusCode()).isGreaterThanOrEqualTo(200).as("返回状态码校验");}

测试报告

如下图,用例相关接口的请求信息、返回信息也都由框架自动记录在了报告中,如有其它需要内容输出到测试报告,可以在用例中添加 Report.log("要添加内容")。

图片

其它

  • 配置:如其它 spring 工程,配置文件在 resources 目录下,类似 pre、test 区分不同环境,application.properties 中定义一般的配置信息(和环境无光),其中 pring.profiles.active=pre 来切换不同环境

  • 测试范围定义:测试用例由testng维护,如框架中所示,详细使用方法参见testng官网(https://testng.org/doc/documentation-main.html#testng-xml)

    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite verbose="1" name="bulls-test" ><listeners>
        <listener class-name="com.bulls.qa.service.CustomListener"></listener>
        <listener class-name="com.bulls.qa.service.NoticeListener"></listener></listeners><test name="bulls自动化" preserve-order="true">
        <parameter name="reruntimes" value="0"></parameter>
        <packages>
        </packages>
        <classes>
            <class name="com.bulls.qa.testcase.testerhome.Demo">
                <methods>
                    <include name="test"></include>
                </methods>
            </class>
        </classes></test></suite>

  • 运行:项目入口 com.bulls.qa.BullsApplication.main

//打包
mvn clean -DskipTests=true  package
//运行
java -jar target/bulls-0.6-SNAPSHOT.jar  测试范围配置文件.xml  

如上面例子,测试范围配置文件可以配置多个,执行时指定测试范围,如不指定默认使用打包的程序代码中的测试范围配置文件

  • 测试报告:测试报道为单html文件,方便jenkins配置展示,报告地址运行时所在目录下bulls.html

  • 断言,选用的断言框架为 AssertJ,AssertJ 的强大无需赘述,详细使用方法参见 AssertJ 官网(https://assertj.github.io/doc/)

assertThat(response.jsonPath().getList("recommendations")).size().isGreaterThan(0).as("recommendations长度大于0");assertThat(response.jsonPath().getBoolean("has_more")).isTrue().as("has_more为true");assertThat(response.jsonPath().getList("recommendations")).as("recommendations长度大于0").size().isEqualTo(3);List<String> types = JsonPath.from(response.asString()).getList("recommendations.item_type");String[] strs = "product,product-ad-card,deal,ad,shopping-curated-collection,auto-generated-collection,video,campaign-banner,benefit,web-view".split(",");assertThat(strs).containsAll(types).as("types在枚举范围内");

  • 发送测试结果消息通知,参见代码 NoticeListener,具体根据需要自行扩展

  • 接口传参设置,较复杂的接口参数设置

相关接口定义

- name: 编辑商品
  id: itemEdit
  path: http://$mnghost/item/edit
  method: post
  cookies: $XXXXXXCookies
  headers: >-
    \{"User-Agent":"$UA","Content-Type": "application/json"\}
  parameters: >-
    \{"itemId":"2904"\}- name: 添加商品
  id: itemSave
  path: http://$mnghost/item/save
  method: post
  cookies: $XXXCookies
  headers: >-
    \{"User-Agent":"$UA","Content-Type": "application/json"\}
  parameters: >-
    \{"itemId":"2913","categoryIdList":[1],"topCategoryName":"美食","itemName":"autoTest goods","limitNumber":3,
    "priceText":"","countDownCycle":"3","countDownLimit":"1","itemNo":"12sqw","delivery":"MANUAL",
    "image":"//yun.XXXXXX.com/images/202005/4su03vvahd.jpg","detail":"","itemStatus":"ON","skuProperties":[],
    "skuList":[{"id":3375,"stock":999999,"stockId":null,"sellingPrice":100,"originalPrice":100,"costPrice":100,
    "realPayPrice":100,"properties":null,"skuNo":"1","skuEnable":true,"changeStock":0}],"supportCOD":true,
    "originItemId":null,"merchantId":73,"tagIds":[],"id":2913,"topCategoryId":1,"itemShortName":"autoTest goo","url":null,
    "minPrice":100,"stock":0,"isRecommend":false,"minSkuOriginalPrice":null,"minSkuPriceDiff":null,"maxPriceDiff":null,
    "maxPriceDiffPrice":null,"maxPriceDiffOriginalPrice":null,"gmtModified":"2020-06-19 16:57:36","gmtModifyName":"测试专用",
    "gmtModifyEmail":"test@XXXXXX.com.cn","mainRecomIds":null,"merchantName":"autoTestShop01","merchantDelivery":"MANUAL",
    "imgHeight":[{"imgUrl":"http://yun.XXXXXX.com/images/202006/mj3yg07pj8.jpg","height":136},
    {"imgUrl":"http://yun.XXXXXX.com/images/202006/d47ad68hhc.jpg","height":372}],"mainImgUrl":null,"itemIntroduce":null,
    "saleLableUrl":null,"ssoDesc":null\}

相关代码

goodsId = 2904;//编辑接口,获取测试的商品信息Request request = Request.getInstance("itemEdit");//直接设置,key-value形式Response response = request.setParameter("itemId", goodsId).doRequest();//库存小于50,更新库存JsonPath jsonPath = response.jsonPath();if (jsonPath.getBoolean("success") && jsonPath.getInt("data.stock") >= 50) {
    // dosomething}Map<String, Object> map = response.jsonPath().getMap("data");if (map == null) {
    map = new HashMap<>();}map.put("itemId", goodsId);map.put("stock", 9999999);request = Request.getInstance("itemSave");//遍历接口的传参结构定义,替换掉key完全匹配的那个map部分request.setParameters(map);//按照json path定位要设置的keyrequest.setParameter("$.skuList[0].stock", 9999999);request.setParameter("$.skuList[0].changeStock", null);//根据路径删除,路径按json pathrequest.removeParameterByPath("$.skuList[0].stockId");request.removeParameterByPath("$.skuList[0].id");request.doRequest();

  • 详尽的json-path使用方法,参见JsonPath使用(https://www.javadoc.io/doc/io.rest-assured/json-path/latest/io/restassured/path/json/JsonPath.html)

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1328830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在芯片设计端,从事DFT岗位是什么体验?

从1975年PHILIPS公司实验中心首次提出“可测性设计”概念至今&#xff0c;DFT已经伴随着芯片走过了半个世纪。 但DFT在数字IC设计岗位中还是显得很神秘很低调。 你说他重要吧&#xff0c;并不是所有芯片设计公司都有这个岗位&#xff0c;你说他不重要吧&#xff0c;但凡芯片产…

数据结构 | 北京大学期末试卷查漏补缺

目录 顺序存储 优点 缺点 适用于&#xff1a; 链式存储 优点 缺点 适用于&#xff1a; 折半查找为什么要使用顺序存储结构 树的存储结构​编辑 对于一个数据结构&#xff0c;一般包括 DFS&BFS 什么是递归程序 C语言不带头结点的单链表逆置 检测字符…

【CMake保姆级教程】制作动静态链接库、指定动静态库输出路径

文章目录 前言一、动静态链接库的介绍1.1 动态链接库 (DLL)1.2 静态链接库 (LIB) 二、制作静态库三、制作动态库四、指定动静态库输出路径4.1 方式1 - 适用于动态库4.2 方式2 - 都适用 总结 前言 在软件开发中&#xff0c;我们经常听到动态链接库&#xff08;Dynamic Link Lib…

c语言易错题之数据类型变换

1.题目 #include<stdio.h> int main() {int arr[]{1,2,3,4,5};short*p (short*)arr;int i 0;for(i0;i<4;i){*(pi)0;}for(i0;i<5;i){printf("%d ",arr[i];}return 0; }2.解析 这道题主要容易错在&#xff0c;大家会以为通过指针赋值的时候&#xff0c;…

基于springboot的日记本系统源码+数据库+安装使用说明

之前写的SpringBoot日记本系统备受好评&#xff0c;考虑到还是有很多小伙伴不会部署&#xff0c;所以这一篇文章就单独来讲一下部署步骤吧。 需要资源 idea&#xff08;破不破解都行&#xff09; MySQL&#xff08;最好5.7以上版本&#xff0c;最好8.0&#xff09; Navicat…

SpringBoot Elasticsearch全文搜索

文章目录 概念全文搜索相关技术Elasticsearch概念近实时索引类型文档分片(Shard)和副本(Replica) 下载启用SpringBoot整合引入依赖创建文档类创建资源库测试文件初始化数据创建控制器 问题参考 概念 全文搜索&#xff08;检索&#xff09;&#xff0c;工作原理&#xff1a;计算…

Node.js-模块化(二)

1. 模块化的基本概念 1.1 什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层将系统拆分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 1.2 编程领域中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&…

Spring源码分析 @Autowired 是怎样完成注入的?究竟是byType还是byName亦两者皆有

1. 五种不同场景下 Autowired 的使用 第一种情况 上下文中只有一个同类型的bean 配置类 package org.example.bean;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class FruitCo…

git首次使用--去公司第一次拉取

文章目录 一&#xff0c; 在企业中首次拉取项目二&#xff0c;提交项目1. 提交----新添加的文件2. 将分支上的代码同步到master3. 提交----更改后的文件 三&#xff0c;常见问题1. Git Pull Failed 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一&am…

【组合数学】Pólya 计数理论

目录 1. 引言2. 置换群3. Burnside 引理共轭类k 不动置换类Burnside 引理 4. Plya 计数定理4.1 对点着色问题4.2 对面着色问题4.3 重复球放盒子 1. 引言 Plya 计数理论是数学中的一个分支&#xff0c;主要研究的是对称性在组合计数问题中的应用。该理论以匈牙利数学家乔治波利…

《Python Advanced Programming + Design Patterns + Clean Code》

清洁代码 — 学习如何编写可读、可理解且可维护的代码 高级Python编程知识 Python之常用设计模式 Advanced Programming装饰器 decorators生成器 & 迭代器with 上下文管理器面向对象Mixin 模式反射机制并发编程 Design Patterns设计模式分类简单工厂模式工厂模式 √抽象工厂…

指标体系构建-02-从0开始,梳理数据指标体系

指标体系构建-02-从0开始&#xff0c;梳理数据指标体系 一个例子&#xff0c;看懂并列式指标梳理 并列式指标体系&#xff0c;一般用于&#xff1a;描述个体情况 当我们想从几个不同角度&#xff0c;描述问题的时候&#xff0c;就需要并列关系 举个栗子&#x1f330;&#xf…

安全基础~实战应用

文章目录 HTTP请求头应用X-Forwarded-ForHTTP动作练习(修改请求方式)浏览器信息伪造(修改User-Agent)来源请求伪造(referer应用) 密码的应用SQL注入漏洞测试(前部分)PHP_encrypt_1(ISCCCTF) XShell连接Linxu连接Windows连接 HTTP请求头应用 X-Forwarded-For 原理作用 一般的…

浅谈在线监测系统与配电能效平台在供水水厂的应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201800 【摘要】针对自来水厂工艺老化资金有限的问题&#xff0c;设计水厂在线监测系统&#xff0c;采用安科瑞&#xff0c;对原水滤后水、出厂水进行采样分析&#xff0c;并通过基于组态的上位机系统实现水质数据的实时监测。该系统…

深入了解UI标签栏设计细节:你不能错过的要点

UI 标签栏的作用有哪些&#xff1f; 导航是移动 UI 中最常见的组成部分&#xff0c;通常放置在 UI 标签栏上&#xff0c;以帮助我们在不同的页面之间切换。UI 标签栏可以保持界面的可控性&#xff0c;并提高可用性。简而言之&#xff0c;UI 标签栏可以加强交互&#xff0c;让用…

渲染图和效果图的一样吗?渲染图与效果图区别?

在建筑、设计及电影制作等一系列领域&#xff0c;你可能经常听说渲染图和效果图这两个词汇。它们虽然在视觉表现上有许多相似之处&#xff0c;但在实质上却有着极其不同的特性和用途。此文主要探讨提供优质效果图云渲染服务&#xff0c;以及渲染图与效果图之间的区别。 一、 效…

快递收发线上管理教程

前台快递收发几乎是每家公司行政前台的“必修课”&#xff0c;所以网络上制度模板满天飞&#xff0c;但现实中能彻底解决快递收发管理难题的几乎为零&#xff0c;那前台快递收发管理&#xff0c;究竟要如何才能摆脱制度的桎梏&#xff1f; 纵观各种前台快递收发制度范本&#…

Java可变参数(学习推荐版,通俗易懂)

定义 可变参数本质还是一个数组 示例代码 注意事项 1.形参列表中&#xff0c;可变参数只能有一个 2.可变参数必须放在形参列表的最后面 注意是最后面。 name也可以为int类型

Vuex的学习-2

Vuex的核心概念 StateMutationAction 1.State State提供唯一的公共数据源&#xff0c;所有共享的数据都统一放在Store的State中进行存储。 const store new Vuex.Store({state : { count: 0 } }) 这是渲染的页面 组件访问数据的第一种方式 组件访问数据的第二种方式 // 1…

数据结构和算法笔记2:二分法

二分法网上有两种写法&#xff0c;一种左闭右闭&#xff0c;一种左闭右开&#xff0c;个人习惯左闭右闭的写法&#xff0c; 有序数组查找数 这是标准二分法&#xff0c;对应力扣的704. 二分查找&#xff1a; 求值为target的索引 int search(vector<int>& nums, i…