写点什么

干货 | 通用 api 封装实战,带你深入理解 PO

  • 2022 年 8 月 29 日
    北京
  • 本文字数:2805 字

    阅读完需:约 9 分钟

在普通的接口自动化测试中,如果接口的参数,比如 url,headers 等传参改变,或者测试用例的逻辑、断言改变,那么整个测试代码都需要改变。apiobject 设计模式借鉴了 pageobject 的设计模式,可以实现一个优雅、强大的接口测试框架。


理念


apiobject 设计模式可以简单分为 6 个模块,分别是 API 对象、接口测试框架、配置模块、数据封装、Utils、测试用例。


  • 接口测试框架:base_api,完成对 api 的驱动

  • API 对象:继承 base_api 后,完成对接口的封装

  • 配置模块:完成配置文件的读取

  • 数据封装:数据构造与测试用例的数据封装

  • Utils:其他功能封装,改进原生框架不足

  • 测试用例:调用 Page/API 对象实现业务并断言枯燥的讲述概念可能难以理解,后面的章节都会围绕这些模块进行理论的拆解和实例的演示。


api 模式应用


在这里将会结合企业微信的部门管理,获取部门列表接口作为一个接口测试用例,从没有封装到使用 apiobject 设计模式进行封装改造。将实战与理论结合,更深入理解 apiobject 设计模式。


环境准备企业微信服务端 API:接口文档 - 企业微信开发者中心


import requests
class TestDemo:
def test_get_token(self):
r = requests.get(url="https://qyapi.weixin.qq.com/cgi-bin/gettoken",
params={"corpid": "ww93348658d7c66ef4", "corpsecret": "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw"})
return r.json()["access_token"]
def test_department_list(self):
r = requests.get(url="https://qyapi.weixin.qq.com/cgi-bin/department/list",
params={"access_token": self.test_get_token(), "id": 1})
assert r.json()["errcode"] == 0
return print(r.json())
复制代码


思路

  • apibase_api.py 是用来封装所有 api 的通用方法,比如打印 log、对断言工具做二次封装等,不牵涉和业务相关的操作 wework.py 继承 base_api 并实现基本业务,之后所有的具体的业务资源继承自 wework,比如 token 的获取等;department 继承自 wework,用来实现对应模块具体的业务逻辑,比如发送请求,请求内有什么参数等等。

  • testcases 文件夹内统一存放所有的测试用例,调用 API 对象实现业务并断言

  • utils 文件夹内存放对其他功能封装,改进原生框架不足

  • data 文件夹数据构造与测试用例的数据封装此外,还有配置模块与数据封装会在后面的章节进行具体的介绍


实战案例

utils.py,在此文件中封装一个 jsonpath 方法。

import json
from jsonpath import jsonpath
class Utils:
@classmethod
def jsonpath(cls, json_object, expr):
return jsonpath(json_object, expr)
复制代码

base_api.py,在此文件中调用 utils 中的 jsonpath 方法。

from test_wework.utils.Utils import Utils
class BaseApi:
json_data = None
def jsonpath(self, expr):
return Utils.jsonpath(self.json_data, expr)
复制代码

wework.py,继承类 BaseApi,实现 token 的获取。将在后面“通用 api 封装”章节中详细讲述函数内部的设计。

class WeWork(BaseApi):
corpid = "ww93348658d7c66ef4"
contact_secret = "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw"
token = dict()
token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
@classmethod
def get_token(cls, secret=contact_secret):
# 避免重复请求,提高速度
if secret not in cls.token.keys():
r = cls.get_access_token(secret)
cls.token[secret] = r["access_token"]
return cls.token[secret]
@classmethod
def get_access_token(cls, secret):
r = requests.get(cls.token_url, params={"corpid": cls.corpid, "corpsecret": secret})
return r.json()
复制代码


department.py,继承类 WeWork,发起一个 get 请求,获取 department 的 list。

class Department(BaseApi):
list_url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"
def list(self, id):
self.json_data = requests.get(self.list_url, params={"access_token": WeWork.get_contact_token(), "id": id}).json()
return self.json_data
复制代码


test_department.py,断言返回值中的第一个 name 是否为“WestWayyt”。


class TestDepartment:
department = Department()
def test_department_list(self):
r = self.department.list(1)
assert self.department.jsonpath(expr="$..name")[0] == "WestWayyt"
复制代码


通用 api 封装实战

在 apiobject 设计模式中,需要一个“base_api”作为其他 api 步骤的父类,把通用功能放在这个父类中,供其他的 api 直接继承调用。这样做的优点在于,减少重复代码,提高代码的复用性。

上文在演示使用 api-object 设计模式对脚本进行改造时提到了 base_api。不过在上文,仅仅只是封装了一个 utils 中的一个简单方法。并没有完全体现出 base_api 的实际作用。

接下来会通过通用接口协议的定义与封装实战,实际体会一下 base_api 的巧妙之处。

base_api.py,在代码内,对 request 进行一层封装,当然在这里还看不出来具体的优势:

import requests
class BaseApi:
def request(self, method, url, **kwargs):
self.json_data = requests.request(method=method, url=url, **kwargs)
return self.json_data
复制代码

wework.py,继承于类 BaseApi,可以直接调用父类中的 request 方法(不需要导入 requests 库),从而发起一个 get 请求:

from test_interface.test_wework.api.base_api import BaseApi
class WeWork(BaseApi):
corpid = "ww93348658d7c66ef4"
contact_secret = "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw"
token = dict()
token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
def get_access_token(self):
r = self.request(method="get", url=self.token_url,
params={"corpid": self.corpid, "corpsecret": self.contact_secret})
return r.json()
复制代码

test_wework.py,继承于类 WeWork,主要目的只是为了检查上面的 get_access_token(self) 是否成功:

from test_interface.test_wework.api.wework import WeWork
class TestWeWork(WeWork):
def test_get_access_token(self):
r = self.get_access_token()
assert r["errcode"]==0
复制代码


在上面的案例中,在 base_api.py 中对 requests 进行了多一层的封装,这样子,只要是属于 BaseApi 这个类的子类,都可以无需引用而直接调用 requests 库。从而发起各种各样的请求,实现了通用接口协议的定义与封装。

免费领取:性能测试+接口测试+自动化测试+测试开发+测试用例+简历模板+测试文档

http://qrcode.testing-studio.com/f?from=infoQ&url=https://ceshiren.com/t/topic/16565


用户头像

社区:ceshiren.com 微信:ceshiren2021 2019.10.23 加入

微信公众号:霍格沃兹测试开发 提供性能测试、自动化测试、测试开发等资料,实时更新一线互联网大厂测试岗位内推需求,共享测试行业动态及资讯,更可零距离接触众多业内大佬。

评论

发布
暂无评论
干货 | 通用 api 封装实战,带你深入理解 PO_霍格沃兹测试开发学社_InfoQ写作社区