ApiChain 从迭代测试用例到项目回归测试 核心使用教程
项目地址:ApiChain 项目主页
环境变量
环境变量是在特定的开发环境(开发、测试、uat等)下,保存的一份数据集,环境变量是发送网络请求或者执行单测的一个重要数据源。环境变量根据作用范围可以分为全局环境变量、项目环境变量、迭代环境变量和单测环境变量。
环境变量创建
全局环境变量
全局环境变量入口在设置->环境变量->选择环境->添加。全局环境变量在所有的网络请求和单测中都可以作为参数使用,比如测试使用的账户uid,登录的appSessionId等;
项目环境变量
项目环境变量入口在项目->选择项目->环境变量->选择环境->添加。项目环境变量仅在与该项目有关的接口参与发送网络请求或者单测时有效,比如当前项目接口访问的域名前缀 api_host;
迭代环境变量
迭代环境变量入口在迭代->选择迭代->环境变量->选择环境->添加。迭代环境变量是为了完成当前迭代的过程中,进行发送网络请求和单元测试,同时不想污染全局数据源的一个变量容器,是日常开发中最常使用的数据源。在迭代环境变量中还可以通过选择项目,为迭代中的某个项目特殊指定一些环境变量的值,优先级为 迭代+项目 > 迭代 > 项目 > 全局。
单测环境变量
单测环境变量入口在项目->选择项目->单测->选择单测->环境变量->添加。单测环境变量存在于当迭代的单测转变为与特定几个项目关联的项目单测,用于对项目进行回归测试时使用的特定单测用例中。单测环境变量对该单测中涉及的所有步骤流程有效,初始值从迭代环境变量中导出,因此优先级为 单测+项目 > 单测 > 项目 > 全局。
环境变量使用
发送普通网络请求
当你通过 请求->发送请求->选择项目->选择环境 的流程对特定项目进行接口的网络请求时,输入类似 {{api_host}} 的内容时,实际上在引用 key 为 api_host 的环境变量,在这种情况下,能够使用的环境变量为项目环境变量和全局环境变量,优先级为 项目环境变量 > 迭代环境变量。
从历史记录搜索发送网络请求
当你通过 请求->请求记录->选择项目->选择环境 的流程搜索到网络请求的发送记录,点击查看按钮发送网络请求时,效果通发送不同网络请求,只是数据都已填充好,在这种情况下,能够使用的环境变量为项目环境变量和全局环境变量,优先级为 项目环境变量 > 迭代环境变量。
从迭代入口发送网络请求
当你通过 迭代->选择迭代->文档->发送请求 的流程从迭代的入口进入到发送网络请求的页面时,在这种情况下,能够使用的环境变量为迭代环境变量、项目环境变量和全局环境变量,优先级为 迭代+项目 > 迭代 > 项目 > 全局。
执行迭代单测
当你通过 迭代->选择迭代->单测 入口添加或者修改单测,根据项目和URI选择单测的接口,填充单测数据时,可以选择的一个数据源是环境变量,此时的环境变量能够获取当前版本迭代的数据,因此优先级为 迭代+项目 > 迭代 > 项目 > 全局。
执行项目单测
当你在项目中执行单测时,单测环境变量生效,初始值从迭代环境变量继承,优先级为 单测+项目 > 单测 > 项目 > 全局。可以点击图中入口进行修改,修改后只对当前单测有效。
发送网络请求
发送网络请求的入口
直接发送网络请求
从导航的请求->发送请求,选择项目和环境,可以对选中的项目在特定开发环境下的任意接口发送网络请求,请求的域名是 项目环境变量 下配置的 api_host 这个key的内容。
从历史发送记录发送网络请求
发送过的网络请求,下次想要参数稍作修改,再次发送请求,可以通过 请求->请求记录,按项目、环境、时间范围等条件搜索出历史发送过的网络请求,点击查看详情按钮,进入到发送网络请求页面。此时效果同 直接发送网络请求 一致,不过接口Url和参数都已填充好,可以稍作修改即可发送网络请求。
从单测执行记录发送网络请求
在迭代->选择一个迭代->单测->选择一个单测->执行记录->任选一条执行记录->查看详情 可以看到这次单测所有步骤的接口网络请求和返回的数据。
点击 任意一个步骤的请求地址,可以进入到请求发送页面,能够复现当时单测的请求数据,通过发送网络请求进行接口调试,所有数据都是当时单测使用的数据。原理是执行单测的网络请求数据存入到了历史发送记录中了,效果同上面 从历史发送记录发送网络请求 一致。
从迭代文档列表发送网路请求
通过 迭代->文档->发送请求 的路径进入到请求发送页面,与 直接发送网络请求 相比,预先内置了 版本迭代 这个容器,于是选择项目只能在这个迭代涉及的项目中选择,保存接口到迭代文档中时不需要选择保存到哪个迭代,同时网络请求的参数可以使用属于这个版本迭代的环境变量了。
从迭代文档详情发送网络请求
在迭代文档的详情页面点击发送请求,可以使用迭代文档的示例数据作为请求的发送数据,同时与从历史记录发送请求相比,可以使用属于这个迭代的环境变量。
构造请求的数据块
一个完整的请求由四块数据构成,分别是 路径变量、参数、头部、主体。
路径变量
路径变量可以方便的构建动态的URI,在地址栏中规定出URI的具体格式,使用{{}}括起来的字符串定义路径参数中的变量,在下面table中定义变量的具体值。这些值可以是固定的数据,对环境变量的引用或者是使用 内置函数 生成的数据,这是和PostMan在发送网络请求方面最大的不同。
参数
这个填充到请求的query_string中,和PostMan一样
头部
这个填充到请求的header中,和PostMan一样
主体
这个填充到请求的body中,和PostMan一样
Content-Type
urlencoded
这个是正常post请求的发送方式
form-data
选中这个可以在body填充数据时支持文件上传
application/json
复杂的body数据上传可以使用原生json提交,此时接口文档的编写会解析该json,生成一个table,你可以填写每一层数据的含义。
内置函数
如果没有内置函数,你写的网络请求或者单测用例都是死的,无法重复利用。不同的内置函数根据不同的规则生成随机不重复的数据,用于网络请求的数据填充。环境变量和内置函数可以应用于发送网络请求和编写单测用例。
在table中填写值时,输入 {{ 可以调用出所有可以使用的预制的数据,包括环境变量和内置函数,输入 {{$ 可以调出所有内置函数生成你想要的数据(因为我以前是写PHP的😄),全量支持的内置函数如下:
$randomString
通过uuid生成全局唯一的字符串,个人最常用的内置函数。比如新建文件夹这个接口,需要传入文件夹名称,每次请求都自己手动填写很累,还容易出现重复数据破坏了单测的随机不重复原则,为了让每次网络请求或者执行单测,都能使用不同的文件夹名称开始整个流程。
$randomInt
生成随机int类型数据
$randomLong
生成随机long类型数据
$currentDateTimeYmdHis
生成 YYYY-MM-DD HH:ii:ss 格式的日期时间数据,每次生成都是基于当前时间,如 2024-11-21 16:11:11
$currentDateYmd
生成 YYYY-MM-DD 格式的日期数据,每次生成都是基于当前时间,如 2024-11-21
$currentDateTimeIntYmdHis
带有int的内置函数代表返回值是个数值而不是字符串,该内置函数用于生成 YYYYMMDDHHiiss 格式的日期时间数据,每次生成都是基于当前时间,如 20241121161111
$currentDateIntYmd
带有int的内置函数代表返回值是个数值而不是字符串,该内置函数用于生成 YYYYMMDD 格式的日期数据,每次生成都是基于当前时间,如 20241121
$currentTimestampSecond
返回秒级的当前时间戳
$currentTimestampMicrosecond
返回毫秒级的当前时间戳,对上面秒级的时间戳×1000
编写迭代单测用例
写单测用例,就像画一幅有向不循环的图,图中的每个节点是这个单测用例的每一个步骤,连线代表着数据的流向,这幅图通常有一个或者多个起点,但通常只有一个终点。起点的数据来源于 环境变量、内置函数或者固定数据,经过特定环境、特定项目的接口“加工”后,输出新的数据。这幅有向不循环的图其他节点的数据来源,相对于起点节点,可以引用前面执行过的那些步骤使用过或者输出来的数据,也就是引用前面步骤的 路径变量、参数、头部、主体、返回值 的数据。
如上图所示的单测用例,上传制品包会返回制品id,可以根据制品id拿到制品的30分钟有效期的下载token,根据制品token可以下载制品,单测用例正是将这套流程涉及的一系列接口调用固定下来,称为可重复执行某个业务目标的利器。
如上图 查询制品 的步骤传参,需要使用第一个 上传ios包 返回的制品id作为数据,同时他还是用了一个全局环境变量 appSessionId 作为参数。引用上一个步骤的参数的方式如下图所示:
数据源选择 步骤参数/返回值,步骤选择 上传鸿蒙包 ,使用上面步骤的 response 中的数据中的某个内容作为当前接口的传参,下面会贴心的给出这个数据源数据格式的一个demo。(没错,来自于接口文档的demo)。用 点号 (.)语法找出json路径,这里是 data.artifactId。
每一个执行步骤都肩负着一个重要“使命”,就是校验整个单测用例是否是可靠的,是否有必要继续执行接下来的用例流程。也就是拿当前步骤的执行结果与前面某个步骤的返回数据进行“对照”,一个最基本的校验方式是要求当前接口返回的code必须是成功的(非返回json格式数据的接口可以不做校验,默认通过)。所以每个接口可以有1个或多个的校验整个单测是否成功的“门禁”。
校验结果与上面填充数据的区别,一个是可以有多个校验规则,他们之间是且的关系,任何一个门禁没通过,整个单测失败。另一个点是 每个填充只有一个数据,而校验则是一个等式或者不等式,会有左右两个数据源,左边数据源通常从当前步骤的执行结果取数据,右边数据源从前面步骤或者环境变量或者固定数据。
单测特有的内置函数
由于单测需要使用前面步骤的数据作为当前步骤的参数或者整个单测流程是否成功的校验依据,这往往涉及到能够对前面的数据做处理,产生出新的满足我们用途的新数据。目前提供了以下三个仅用于单测的内置函数:
*first()
针对前面的数据源的数据包含json数组的情况,我们需要取数组的第一个数据(通常是一个对象),在这个对象的基础上,继续我们的json path,直到最终取到目标数据为止,一下是一个demo:
*{{current_step.response.data.first().target}} 这个取当前步骤的response这个json的data数组的第一个json对象的target 字段的值。
*random()
为了使用单测独特的 随机性 特征,让单测具备通用性,有时候需要取前面步骤 json path 下的一个数组的任意一个对象,取对象中的数据,比如我们查询天气预报这个示例,从城市列表接口任意取一个城市查询当前天气就是这样。*result.random().city 。
*eval()
以上两个函数都是针对数组生效的,下面那个函数是针对字符串生效的,也更强大、更难用。 比如前面步骤返回了类似 https://rf-uat-dmz.xxx.xxxurl.cn/rx/hmInstall?artid=dsfdsfd&token=sdfsfd 这样的数据,我们后面步骤需要从这个步骤中的这个返回中提取出token这个有特殊意义的的数据,可以使用下面的数据***{{__pointed_step__54b05acd-6a86-4bce-93e1-ca884b610486.response.data.locationUrl.eval(‘split(“?”)[1].split(“&”)[1].split(“=”)[1]’)}}**。
eval隐式的左侧数据源是待处理的字符串,内部的处理过程以字符串的形式自己写js代码,处理过程就是用点号(.)作为这个字符串的分割,迭代处理原始字符串。相信作为程序员的你很快就会学会使用这个万能的字符串处理函数 eval 函数的。