测试开发 | 通用 api 封装实战,带你深入理解 PO

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

image1080×434 66.5 KB

在普通的接口自动化测试中如果接口的参数比如 urlheaders等传参改变或者测试用例的逻辑、断言改变那么整个测试代码都需要改变。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())

思路

  • api
    • base_api.py是用来封装所有api的通用方法比如打印log、对断言工具做二次封装等不牵涉和业务相关的操作
    • wework.py继承base_api并实现基本业务之后所有的具体的业务资源继承自wework比如token的获取等
    • department继承自wework用来实现对应模块具体的业务逻辑比如发送请求请求内有什么参数等等。
  • testcases文件夹内统一存放所有的测试用例调用API对象实现业务并断言
  • utils文件夹内存放对其他功能封装改进原生框架不足
  • data文件夹数据构造与测试用例的数据封装此外还有配置模块与数据封装会在后面的章节进行具体的介绍

image1080×661 48.8 KB

实战案例

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 库。从而发起各种各样的请求实现了通用接口协议的定义与封装。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6