快速打造属于你的接口自动化测试框架

1 接口测试

接口测试是对系统或组件之间的接口进行测试,主要是校验数据的交换,传递和控制管理过程,以及相互逻辑依赖关系。
接口自动化相对于UI自动化来说,属于更底层的测试,这样带来的好处就是测试收益更大,且维护成本相对来说较低,是我们进行自动化测试的首选

2 框架选型

目前接口自动化的框架比较多,比如jmeter,就可以集接口自动化和性能测试于一体,该工具编写用例效率不高;还有我们常用的postman,结合newman也可以实现接口自动化;Python+unittest+requests+HTMLTestRunner 是目前比较主流的测试框架,对python有一定的编码要求;
本期我们选择robotframework(文中后续统一简称为RF)这一个比较老牌的测试框架进行介绍,RF是一个完全基于 关键字 测试驱动的框架,它即能够基于它的一定规则,导入你需要的测试库(例如:其集成了selenium的测试库,即可以理解为操作控件的测试底层库),然后基于这些测试库,你能应用TXT形式编写自己的关键字(支持python和java语言,这些关键字即你的库组成),之后,再编写(测试用例由测试关键字组成)进行测试;他支持移动端、UI自动化和接口自动化的测试

3 环境搭建

  • python的安装:目前选取的python3以上的版本,RF的运行依赖python

  • robotframework:参考https://www.jianshu.com/p/9dcb4242b8f2

  • jenkins:用于调度RF的用例执行环境

  • gitlab:代码仓库

4 需求

4.1 需求内容
接口内容:实现一个下单,并检查订单状态是否正常的场景;该需求涉及到如下三个接口

  • 下单接口

  • 订单结果查询接口

  • 下单必须带上认证标识,生成token的接口

环境覆盖:需要支持能在多套环境运行,比如测试和预发布环境
系统集成:需要能够集成在CICD中,实现版本更新后的自动检测

4.2 用例设计
4.2.1 用例设计,根据业务场景设计测试用例,方便后续实现


4.2.2 测试数据构造,预置不同环境的测试数据,供实现调用

5 整体实现架构

接口测试实现层:在RF,通过引用默认关键字 RequestsLibrary (实现http请求)和通过python自定义关键字来完成用例实现的需求;
jenkins调度:在jenkins上配置一个job,设置好RF用例执行的服务器和发送给服务器相关的RF执行的指令,并且在jenkins中配置好测试报告模板,这样用例便可以通过jenkins完成执行并发送测试结果给项目干系人;
生成用例执行的API:上图中蓝色部分,就是为了将jenkins的job生成一个可访问api接口,方便被测项目的CICD集成;
集成到被测系统CICD流程:将上面步骤中封装的API配置在被测应用的gitlab-ci.yml中,完成整个接口自动化的闭环

6 RF用例实现

6.1 引用的内置关键字

  • RequestsLibrary 构造http的请求,get|post等请求

getRequests# get请求的入参[Arguments]    ${url_domain}    ${getbody}    ${geturl}    ${getToken}Create session    postmain    ${url_domain}# 定义header的内容${head}    createdictionary    content-type=application/json    Authorization=${getToken}    MerchantId=${s_merchant_id}# get请求${addr}    getRequest    postmain    ${geturl}    params=${getbody}    headers=${head}# 请求状态码断言Should Be Equal As Strings    ${addr.status_code}    200${response_get_data}    To Json    ${addr.content}# 返回http_get请求结果Set Test Variable    ${response_get_data}   Delete All Sessions

6.2 自定义关键字

  • getEnvDomain 用于从自定义的configs.ini文件获取对应环境的微服务的请求域名
    configs.ini的内容

# 获取configs.ini的内容import configparserdef getEnv(path,env):config = configparser.ConfigParser()config.read(path)passport = config[env]['passport']stock=config[env]['stock']finance=config[env]['finance']SUP = config[env]['SUP']publicApi = config[env]['publicApi']publicOrder = config[env]['publicOrder']data_dict={'passport':passport,'stock':stock,'finance':finance,'SUP':SUP,'publicApi':publicApi,'publicOrder':publicOrder}return data_dict
  • excelTodict 用户将excel中的内容作为字典返回

import xlrd'''通用获取excel数据@:param path excel文件路径@:param sheet_name excel文件里面sheet的名称 如:Sheet1@:env 环境,是IT还是PRE'''def getExcelDate(path, sheet_name,env):bk = xlrd.open_workbook(path)sh = bk.sheet_by_name(sheet_name)row_num = sh.nrowsdata_list = []for i in range(1, row_num):row_data = sh.row_values(i)data = {}for index, key in enumerate(sh.row_values(0)):data[key] = row_data[index]data_list.append(data)data_list1 = []for x in data_list:#print('这是'+str(x))if(x.get('env')==env):data_list1.append(x)return data_list1
  • getToken 提供接口下单的授权token

*** Keywords ***# 根据传入的clientid、secret生成对应的tokengetToken[Arguments]    ${client_id}    ${client_secret}    ${url_domain}Create session    postmain    ${url_domain}${auth}    createdictionary    grant_type=client_credentials    client_id=${client_id}    client_secret=${client_secret}${header}    createdictionary    content-type=application/x-www-form-urlencoded${addr}    postRequest    postmain    /oauth/token    data=${auth}    headers=${header}Should Be Equal As Strings    ${addr.status_code}    200${responsedata}    To Json    ${addr.content}${access}    Get From Dictionary    ${responsedata}    access_token${token}    set variable    bearer ${access}Set Test Variable    ${token}Delete All Sessions
  • getAllDate 获取该用例下的所有数据

getAllData[Arguments]    ${row_no}getEnvDomaingetBalance    ${row_no}getStockNum    ${row_no}getSupProPrice    ${row_no}getProPrice    ${row_no}Set Test Variable    ${publicOrderUrl}Set Test Variable    ${FPbalance}Set Test Variable    ${Pbalance}Set Test Variable    ${Sbalance}Set Test Variable    ${Jbalance}Set Test Variable    ${Cardnum}Set Test Variable    ${sprice}Set Test Variable    ${price}Set Test Variable    ${j_merchant_id}Set Test Variable    ${s_merchant_id}Set Test Variable    ${stock_id}Set Test Variable    ${p_product_id}Set Test Variable    ${s_product_id}
  • 实现demo

*** Settings ***Test TemplateResource          引用所有资源.txt*** Test Cases ****** Settings ***Test TemplateResource          引用所有资源.txt*** Test Cases ***01 下单卡密直储商品[Tags]    orderLOG    ---------------------获取下单前的数量、余额------------------------------------------getAllData    0${Cardnum1}    set variable    ${Cardnum}${FPbalance1}    set variable    ${FPbalance}${Pbalance1}    set variable    ${Pbalance}${Sbalance1}    set variable    ${Sbalance}${Jbalance1}    set variable    ${Jbalance}${CustomerOrderNo1}    Evaluate    random.randint(1000000, 9999999)    random${Time}    Get Timelog    ------------------------下单操作-------------------------------------------------------getToken    100xxxx    295dab07a9xxxx9780be0eb95xxxx   ${casUrl}${input_cs}    create dictionary    memberId=${j_merchant_id}    clientId=1xxx079    userId=string    shopType=string    customerOrderNo=${CustomerOrderNo1}...    productId=${p_product_id}    buyNum=1    chargeAccount=otest888888    notifyUrl=string    chargeIp=string    chargePassword=string...    chargeGameName=string    chargeGameRole=string    chargeGameRegion=string    chargeGameSrv=string    chargeType=string    remainingNumber=0...    contactTel=string    contactQQ=string    customerPrice=0    poundage=0    batchNumber=    originalOrderId=string...    shopName=string    appointSupProductId=0    stemFromSubOrderId=123456    externalBizId=456789postRequests    ${publicOrderUrl}    ${input_cs}    /api/Order    ${token}${data}    get from dictionary    ${responsedata}    data${orderid}    get from dictionary    ${data}    idsleep    6${getdata}    create dictionary    Id=${orderid}    PageIndex=1    PageSize=1getRequests    ${publicOrderUrl}    ${getdata}    /api/Order/GetList    ${token}${datalist}    get from dictionary    ${response_get_data}    data${data}    get from dictionary    ${datalist}    list${dict}    set variable    ${data}[0]${orderOuterStatus}    get from dictionary    ${dict}    orderOuterStatusLOG    ---------------------获取下单后的数量、余额----------------------------------------------getAllData    0${Cardnum2}    set variable    ${Cardnum}${FPbalance2}    set variable    ${FPbalance}${Pbalance2}    set variable    ${Pbalance}${Sbalance2}    set variable    ${Sbalance}${Jbalance2}    set variable    ${Jbalance}${sprice}    set variable    ${sprice}${price}    set variable    ${price}log    ------------------断言-----------------------------------------------------------------${Cardnum3}    Evaluate    ${Cardnum1}${Jbalance3}    Evaluate    ${Jbalance1}${Sbalance3}    Evaluate    ${Sbalance1}${Pbalance3}    Evaluate    ${Pbalance1}should be true    ${orderOuterStatus}==90should be true    ${Cardnum3}==${Cardnum2}should be true    ${Jbalance3}==${Jbalance2}should be true    ${Sbalance3}==${Sbalance2}should be true    ${Pbalance3}==${Pbalance2}

7 集成到CICD流程

7.1 jenkins配置job
通过jenkins的参数化构建,定义it和pre两套环境

jenkins发送RF执行的命令

7.2 封装的jenkins_job的执行接口地址
通过python的flask框架,根据测试和pre两套环境包一层jenkins的job执行接口

__author__ = 'paul'# !/usr/bin/env python# -*- coding: utf-8 -*-from flask import Flask, abort, request, jsonifyimport jenkinsserver = jenkins.Jenkins('http://10.0.1.xxx:80', username='xxx', password='fuluxxxx')app = Flask(__name__)tasks = []# it的测试集合http请求接口@app.route('/test/it', methods=['get'])def robot_Test_It():server.build_job('CI_FuluOrder', {'environment': 'IT'})return jsonify({'result': 'success'})# pre的测试集合http请求接口@app.route('/test/pre', methods=['get'])def robot_Test_Pre():server.build_job('CI_FuluOrder', {'environment': 'PRE'})return jsonify({'result': 'success'})if __name__ == "__main__":# 将host设置为0.0.0.0,则外网用户也可以访问到这个服务app.run(host="0.0.0.0", port=80, debug=True)

7.3 将上述flask封装的接口打包成镜像
根据dockerfile生成镜像

FROM python:3.6WORKDIR /appEXPOSE 80COPY .  .RUN pip install -r requirements.txtENTRYPOINT ["python","robotTestApi.py"]

7.4 将镜像部署到kubernetes,对外提供服务
供触发测试执行的调用入口 ,这部分封装的接口部署在本地的k8s集群下ordermiddle


IT: http://ordermiddle.xxx.cn/test/it
pre:http://ordermiddle.xxx.cn/test/pre

7.5 被测项目的CICD集成接口自动化测试
gitlab目前采取直接对CICD脚本加入测试步骤,在部署到容器30秒后(考虑到容器在K8S启动时间)调用测试接口

7.6 发送测试报告

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

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

相关文章

Java Number Math 类方法

Java Math 类 Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。 Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。 public class Test { public static void main (String []args…

研发协同平台数据库死锁处理及改进

源宝导读:数据库死锁是高并发复杂系统都要面临课题,处理死锁问题没有一招制敌的标准方法,需要具体问题具体分析。本文将基于研发协同平台遇到的死锁案例,介绍从监控、分析到处理的完整过程和经验总结。一、背景研发协同平台使用的…

Java substring() 方法

substring() 方法返回字符串的子字符串。 语法 public String substring(int beginIndex)或public String substring(int beginIndex, int endIndex)参数 beginIndex – 起始索引(包括), 索引从 0 开始。 endIndex – 结束索引(不包括&…

简单说说async/await

小明用async/await写了几年的异步方法,但总没有完全理解里面的机制,他决定去请教邻居小花。小花听了小明的描述后说:首先你要明白异步的根本是什么?大白话解释异步就是:拉一个人(线程)帮着做一些…

Newtonsoft 六个超简单又实用的特性,值得一试 【下篇】

一:讲故事上一篇介绍的 6 个特性从园子里的反馈来看效果不错,那这一篇就再带来 6 个特性同大家一起欣赏。二:特性分析1. 像弱类型语言一样解析 json大家都知道弱类型的语言有很多,如: nodejs,python,php&am…

C++实现双栈结构(一个顺序表中使用两个栈)

因为平常栈中push的数据不会太多&#xff0c;为了节约空间&#xff0c;所以可以在一个顺序表中使用两个栈 结构图: 在这里我会留一个空间用来判断栈是否满&#xff01; #include <iostream> using namespace std; typedef int ElemType;class DoubleStack { private:El…

Redis凭啥这么快?只能做缓存?架构师道出了真相(颠覆你的认知)

Redis到底有多快Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库&#xff0c;由C语言编写&#xff0c;官方提供的数据是可以达到100000的QPS&#xff08;每秒内查询次数&#xff09;。这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcached 差&…

Java面向对象编程(基础部分)

面向对象编程(基础部分) 类与对象 01&#xff1a; public class ObjectWorkDemo {public static void main(String[] args){Cat cat1 new Cat();cat1.name "Tom";cat1.age 3;cat1.color "white";Cat cat2 new Cat();cat2.name "xiaohua"…

Blazor带我重玩前端(一)

写在前面曾经我和前端朋友聊天的时候&#xff0c;我说我希望有一天可以用C#写前端&#xff0c;不过当时更多的是美好的想象&#xff0c;这一切正变得真实……什么是Blazor我们知道浏览器可以正确解释并执行JavaScript代码&#xff0c;那么浏览器是如何执行C#代码的呢&#xff1…

Java面向对象编程(中级)

面向对象编程(中级) 包 访问修饰符 封装 01: public class Encapsulation01 {public static void main(String[] args){Person person new Person();person.name "Tom";person.setAge(30);person.setSalary(30000);} }class Person {public String name;private…

[Mvp.Blazor] 动态路由与钩子函数

&#xff08;Blazor组件的生命周期函数&#xff09;一直在学习也没有停下脚步&#xff0c;用着脑子还是挺好的&#xff0c;感觉可以更脚踏实地一下。最近偶尔也继续看了看Blazor&#xff0c;毕竟我也开源了一个项目嘛&#xff0c;基本我正式开源的项目都会负责到底&#xff0c;…

Java面向对象编程(高级)

面向对象编程(高级) 类变量和类方法 01: package ChildDemo;public class Child {private String name;public static int cnt 0;public Child(String name){this.name name;}public void join(){System.out.println(name "join the game");} }//package ChildDe…

.NET Core + Kubernetes:Volume

和 Docker 类似&#xff0c;Kubernetes 中也提供了 Volume 来实现数据卷挂载&#xff0c;但 Kubernetes 中 Volume 是基于 Pod&#xff0c;而不是容器&#xff0c;它可被 Pod 中多个容器共享&#xff0c;另外 Kubernetes 中提供比较丰富的 Volume 类型[1]&#xff0c;如&#…

WPF中的Data Binding调试指南

点击蓝字“大白技术控”关注我哟加个“星标★”&#xff0c;每日良时&#xff0c;好文必达&#xff01;WPF中的Data Binding如何Debug?大家平时做WPF开发&#xff0c;相信用Visual studio的小伙伴比较多。XAML代码曾经在某些特殊版本的Visual Studio中是可以加断点进行调试的&…

.NET 5 尝鲜 - 开源项目TerminalMACS WPF管理端支持.NET 5

点击上方“Dotnet9”添加关注哦聊天界面设计TerminalMACS一个使用 Prism 作为模块化框架、基于多个开源控件库作为UI控件选择、集成开源 UI 界面设计的 .NET 5 WPF 客户端项目。项目名称&#xff1a;TerminalMACS WPF管理端项目开源地址&#xff1a;Github&#xff1a;https://…

FreeSql.Generator命令行代码生成器是如何实现的

目录FreeSql介绍FreeSql.GeneratorRazorEngine.NetCore源码解析FreeSql.ToolsFreeSqlFreeSql 是功能强大的对象关系映射技术(O/RM)&#xff0c;支持 .NETCore 2.1 或 .NETFramework 4.0 或 Xamarin。有一个强大的ORM&#xff0c;也方便我们开发一个代码生成器。一般情况下&…

Java IDEA断点调试

断点调试(debug) 断点调试应用案例 01&#xff1a; package Assign;public class Debug01 {public static void main(String[] args) {int sum 0;for (int i 0;i<5;i){sumi;System.out.println(i);System.out.println(sum);}System.out.println("continue");} …

.NET Core请求控制器Action方法正确匹配,但为何404?

【导读】提前预祝各位端午节快乐。有时候我们会发现方法名称都正确匹配&#xff0c;但就是找不到对应请求接口&#xff0c;所以本文我们来深入了解下何时会出现接口请求404的情况。匹配控制器Action方法&#xff08;404&#xff09;首先我们创建一个web api应用程序&#xff0c…

布斯乘法以及带符号数的运算

乘法理解 对于最熟悉的十进制乘法很最基本的运算原理就是一个乘数乘以另一个乘数的个位、十位、百位数字然后求和。比如 放到二进制来看其实它也是这样的&#xff0c;多位数的乘法就是一个乘数乘上另一个乘数的各位求和。那么&#xff1a; 布斯算法及原理 原理 已经知道两…

angular 接入 IdentityServer4

angular 接入 IdentityServer4Intro最近把活动室预约的项目做了一个升级&#xff0c;预约活动室需要登录才能预约&#xff0c;并用 IdentityServer4 做了一个统一的登录注册中心&#xff0c;这样以后就可以把其他的需要用户操作的应用统一到 IdentityServer 这里&#xff0c;这…