前言
本章主要以【企业微信】为例讲述api自动化框架搭建
ps:有空再将代码会上传到github
- github链接:
以前也写过类似简单的接口自动化测试搭建:
如下参考链接:
- 接口自动化测试小demo:https://blog.csdn.net//article/details/123954028?spm=1001.2014.3001.5501
- 接口自动化测试框架搭建:https://blog.csdn.net//article/details/113175512
一、整体框架介绍
apis
-base包:封装api的基础类
-contract包:封装组织的所有api
data包:参数化数据(excel 或者 yml...)
logs包:日志文件
testcase包:封装测试用例类
utils包:工具包,例如读取参数化数据

二、具体框架搭建
Base包:

1、先创建一个base_api.py
为什么要单独封装一个BasePage呢? 如果说以后,request库等这些第三方库底层进行了修改了,我们只需要更改BasePage即可,不会影响到其他类的代码,从某种程度上来说也就是进行解耦
“”"
该类为api测试框架的基类
可以封装框架本身的东西,例如:request
“”"
import requestsfrom mikasa_script.mikasa_auto.workwx_api_autotest.utils.log_utils import logger
class BaseApi:
def send_api(self, req, tools=“requests”):
“”"
对发送接口测试的工具进行封装(可以使用urlib3/requests)
:param tools:
:param req:
:return:
“”"
# 如果没有发送工具,则默认使用requests
# if not tools:
# tools = “requests”
logger.info(f"获取到的工具为:{tools}“)
logger.info(f"获取到的接口发送的数据为:{req}”)
if tools == “requests”:
return requests.request(**req)
2、创建一个workwx_api.py,继承BaseApi
由于以后可能又去测试其他的软件的api,所以我们这边就单独再创建一个workwx_api的类继承BaseApi,为了区分软件api
“”"
该类为企业微信api类
这里可以封装针对企业微信的一些常用方法
“”"
from mikasa_script.mikasa_auto.workwx_api_autotest.apis.base.base_api import BaseApiclass WorkWxApi(BaseApi):
def init(self):
self.token = Nonedef get_token(self, corpid, corpsecret): """ 获取企业微信accesss_token :param corpid: 企业微信后台,我的企业可以找到 :param corpsecret: 企业微信后台,管理工具—通讯录同步可以获取到 :return: """ req = { "method": "GET", "url": "https://qyapi.weixin.qq.com/cgi-bin/gettoken", "params": { "corpid": corpid, "corpsecret": corpsecret } } res = self.send_api(req) self.token = res.json()["access_token"] print("获取到的accessToken:", self.token) return self.token
contract包:

1、创建一个department.py
思路 : 相关的接口放一个类里,例如部门相关接口则创建一个类:department,里面封装部门相关的增删改查接口
企业微信接口文档地址:https://developer.work.weixin.qq.com/document/path/90664
“”"
具体业务类:
该类为企业微信,部门相关接口类
注意:所有的具体业务类,需全部继承自WorkWxApi类
“”"
from mikasa_script.mikasa_auto.workwx_api_autotest.apis.base.workwx_api import WorkWxApi
from mikasa_script.mikasa_auto.workwx_api_autotest.utils.utils import Utilsclass Department(WorkWxApi):
def init(self):
# 获取到yaml文件里的数据,传参调用get_token
yaml_data = Utils.get_yaml_data(“/corp_data.yaml”)
self.get_token(yaml_data.get(“corpid”).get(“mikasa”), yaml_data.get(“secret”).get(“department”))def create_department(self, _id): """ 创建部门 :return: """ req = { "method": "POST", "url": "https://qyapi.weixin.qq.com/cgi-bin/department/create", "params": { "access_token": self.token }, "json": { "name": f"mikasa部门_创建{_id}", "name_en": f"mikasa_create_{_id}", "parentid": 1, "order": 1, "id": _id } } res = self.send_api(req) print("创建部门:", res.json()) return res.json() def update_department(self, _id): """ 修改部门 :return: """ req = { "method": "POST", "url": "https://qyapi.weixin.qq.com/cgi-bin/department/update", "params": { "access_token": self.token }, "json": { "name": f"mikasa部门{_id}", "name_en": f"mikasa_create_{_id}", "parentid": 1, "order": 1, "id": _id } } res = self.send_api(req) return res.json() def get_department(self): """ 获取部门 :return: """ req = { "method": "GET", "url": "https://qyapi.weixin.qq.com/cgi-bin/department/list", "params": { "access_token": self.token } } res = self.send_api(req) print(res.json()) return res.json() def del_department(self, _id): """ 删除部门 :return: """ req = { "method": "GET", "url": "https://qyapi.weixin.qq.com/cgi-bin/department/delete", "params": { "access_token": self.token, "id": _id } } res = self.send_api(req) return res.json()if name == ‘main’:
# Department().create_department(2)
# Department().update_department(2)
Department().get_department()
# Department().del_department(2)
data包:

1、创建corp_data.yaml
该yaml文件封装的是获取accesstoken接口需要的参数
详情可具体看:https://developer.work.weixin.qq.com/document/path/91039
corpid:
mikasa: ww0da7c2c6dbb1d65f
secret:
department: NHkyn5X__aQTidlgH1fJbNtoAxyj1Nv_b9fwW8tnbuc
utils包:

1、创建utils.py
"""
该类为工具类
包括读取yaml文件/读取log日志文件路径
"""
import os
import jsonpath
import yaml
class Utils:
@classmethod
def get_yaml_data(self, yaml_file):
"""
封装提取yaml文件的方法
:param yaml_file: 传入文件的路径
:return:
"""
with open(f"{Utils.get_data_path()}/{yaml_file}", encoding="utf-8") as f:
datas = yaml.safe_load(f)
return datas
@classmethod
def get_data_path(self):
"""
获取data文件路径
:return:
"""
path = os.sep.join([os.path.dirname(os.path.abspath(__file__)), "../data"])
print("path", path)
return path
@classmethod
def get_log_path(cls):
"""
获取日志文件路径
:return:
"""
path = os.sep.join([os.path.dirname(os.path.abspath(__file__)), "../logs"])
print("path", path)
return path
@classmethod
def jsonpath_utils(cls, obj, expt):
"""
json处理工具
:param obj:
:param expt:
:return:
"""
return jsonpath.jsonpath(obj, expt)
if __name__ == '__main__':
path = Utils.get_data_path()
print(Utils.get_yaml_data("/corp_data.yaml"))
2、创建log_utils.py
"""
该类为日志封装类
"""
import logging
import os
# 实例化logger对象
from mikasa_script.mikasa_auto.workwx_api_autotest.utils.utils import Utils
logger = logging.getLogger(__name__)
# 定义日志文件路径
log_path = Utils.get_log_path()
# 判断路径是否存在,不存在就创建
if not os.path.exists(log_path):
os.mkdir(log_path)
# 绑定log的handler
file_handler = logging.FileHandler(filename=f"{log_path}/api_object.log", encoding="utf-8")
# 输出的formatter
formatter = logging.Formatter(
'[%(asctime).19s] %(process)d%(levelname).1s %(filename)s:%(lineno)d:%(funcName)s: %(message)s]')
# 日志格式与句柄的绑定
file_handler.setFormatter(formatter)
# 控制台句柄定义
steam_handler = logging.StreamHandler()
# 日志格式与句柄的绑定
steam_handler.setFormatter(formatter)
# 与logging进行绑定
logger.addHandler(file_handler)
logger.addHandler(steam_handler)
# 设置展示/写入文件的日志的级别
logger.setLevel(logging.INFO)
case包:

1、创建test_department.py
"""
该类为企业微信的部门相关接口的测试用例类
"""
import allure
from mikasa_script.mikasa_auto.workwx_api_autotest.apis.contract.department import Department
from mikasa_script.mikasa_auto.workwx_api_autotest.utils.log_utils import logger
from mikasa_script.mikasa_auto.workwx_api_autotest.utils.utils import Utils
@allure.feature('test_department')
class TestDepartment:
def setup(self):
self.department_id = 1
self.depart = Department()
try:
self.depart.del_department(self.department_id)
except Exception as e:
logger.warning("没有待删除的部门")
def teardown(self):
pass
def setupclass(self):
pass
def teardownclass(self):
pass
@allure.story('test_create_department')
def test_create_department(self):
assert self.depart.create_department(self.department_id)["errcode"] == 0
# logger.info(self.depart.get_department())
obj = self.depart.get_department()
department_list = Utils.jsonpath_utils(obj, "$..department..id")
assert self.department_id in department_list
三、生成测试报告
1、allure命令
# terminal执行allure命令:
pytest test_lover_pad.py --alluredir=./result --clean-alluredir #生成result文件
allure serve ./result #生成报告


2、展示报告
