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

115 阅读4分钟

获取更多技术文章分享

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

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

  • 接口测试框架:base_api,完成对api的驱动
  • API对象:继承base_api后,完成对接口的封装
  • 配置模块:完成配置文件的读取
  • 数据封装:数据构造与测试用例的数据封装
  • Utils:其他功能封装,改进原生框架不足
  • 测试用例:调用Page/API对象实现业务并断言 枯燥的讲述概念可能难以理解,后面的章节都会围绕这些模块进行理论的拆解和实例的演示。

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

企业微信服务端API:work.weixin.qq.com/api/doc/900…

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文件夹数据构造与测试用例的数据封装此外,还有配置模块与数据封装会在后面的章节进行具体的介绍
                                                                        
                                                                        ![](https://ceshiren.com/uploads/default/original/3X/4/f/4f048d64cff9b605ae45769134d0c2ccf56506fd.png)
                                                                        
                                                                        ## 
                                                                        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"
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        ```
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        在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 库。从而发起各种各样的请求,实现了通用接口协议的定义与封装。
                                                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                                                             ## 
                                                                                                                                                                                                                                                                                                                                                             内容全面升级,4 个月 20+ 项目实战强化训练,资深测试架构师、开源项目作者亲授 BAT 大厂前沿最佳实践,带你一站式掌握测试开发必备核心技能(对标阿里P6+,年薪50W+)!直推 BAT 名企测试经理,普遍涨薪 50%+!
                                                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                                                             ⬇️ 点击“阅读原文”,提升测试核心竞争力!
                                                                                                                                                                                                                                                                                                                                                             [原文链接](https://mp.weixin.qq.com/s?__biz=MzU3NDM4ODEzMg==&mid=2247493701&idx=1&sn=bdaf5051eb4c170d04f274945b485bef&chksm=fd318a8eca460398861aaf1f9f4e2512adb2da59866cbec2085781f3bb1d916959c890767fd8#rd) 
                                                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                                                             [获取更多技术文章分享](https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=juejin&timestamp=1650250228
                                                                                                                                                                                                                                                                                                                                                             ) ![img](https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=juejin&timestamp=1650250228)