接口自动化测试框架开发 | Pytest+Allure+AIOHTTP+用例自动生成

测试开发实战技能进阶学习,文末加群!
近期准备优先做接口测试的覆盖,为此需要开发一个测试框架,经过思考,这次依然想做点儿不一样的东西。

  • 接口测试是比较讲究效率的,测试人员会希望很快能得到结果反馈,然而接口的数量一般都很多,而且会越来越多,所以提高执行效率很有必要;
    • 接口测试的用例其实也可以用来兼做简单的压力测试,而压力测试需要并发;
    • 接口测试的用例有很多重复的东西,测试人员应该只需要关注接口测试的设计,这些重复劳动最好自动化来做;
    • Pytest 和 Allure 太好用了,新框架要集成它们;
    • 接口测试的用例应该尽量简洁,最好用 yaml,这样数据能直接映射为请求数据,写起用例来跟做填空题一样,便于向没有自动化经验的成员推广;
    • 加上我对 Python 的协程很感兴趣,也学了一段时间,一直希望学以致用,所以 HTTP 请求我决定用 AIOHTTP 来实现;
      但是 pytest 是不支持事件循环的,如果想把它们结合还需要一番功夫。于是继续思考,思考的结果是其实我可以把整个事情分为两部分;

第一部分,读取 yaml 测试用例,HTTP 请求测试接口,收集测试数据。

第二部分,根据测试数据,动态生成 pytest 认可的测试用例,然后执行,生成测试报告。

这样一来,两者就能完美结合了,也完美符合我所做的设想。想法既定,接着 就是实现了。

第一部分

整个过程都要求是异步非阻塞的

读取 yaml 测试用例

一份简单的用例模板我是这样设计的,这样的好处是,参数名和
aioHTTP.ClientSession().request(method,url,**kwargs)
是直接对应上的,我可以不费力气的直接传给请求方法,避免各种转换,简洁优雅,表达力又强。

        args:  
              - post  
              -       - /xxx/add  
              -     kwargs:  
              -       -  
              -         caseName: 新增 xxx  
              -         data:  
              -           name: ${gen_uid(10)}  
              -     validator:  
              -       -  
              -         json:  
              -           successed: True  
              -     

异步读取文件可以使用 aiofiles 这个第三方库,yaml_load 是一个协程,可以保证主进程读取 yaml 测试用例时不被阻塞,通过await yaml_load()便能获取测试用例的数据

        async def yaml_load(dir='', file=''):  
                """  
                        异步读取 yaml 文件,并转义其中的特殊值  
                                :param file:  
                                        :return:  
                                                """  
                                                        if dir:  
                                                                    file = os.path.join(dir, file)  
                                                                            async with aiofiles.open(file, 'r', encoding='utf-8', errors='ignore') as f:  
                                                                                        data = await f.read()  
                                                                                              
                                                                                                      data = yaml.load(data)  
                                                                                                            
                                                                                                                    # 匹配函数调用形式的语法  
                                                                                                                            pattern_function = re.compile(r'^\${([A-Za-z_]+\w*\(.*\))}$')  
                                                                                                                                    pattern_function2 = re.compile(r'^\${(.*)}$')  
                                                                                                                                            # 匹配取默认值的语法  
                                                                                                                                                    pattern_function3 = re.compile(r'^\$\((.*)\)$')  
                                                                                                                                                          
                                                                                                                                                                  def my_iter(data):  
                                                                                                                                                                              """  
                                                                                                                                                                                          递归测试用例,根据不同数据类型做相应处理,将模板语法转化为正常值  
                                                                                                                                                                                                      :param data:  
                                                                                                                                                                                                                  :return:  
                                                                                                                                                                                                                              """  
                                                                                                                                                                                                                                          if isinstance(data, (list, tuple)):  
                                                                                                                                                                                                                                                          for index, _data in enumerate(data):  
                                                                                                                                                                                                                                                                              data[index] = my_iter(_data) or _data  
                                                                                                                                                                                                                                                                                          elif isinstance(data, dict):  
                                                                                                                                                                                                                                                                                                          for k, v in data.items():  
                                                                                                                                                                                                                                                                                                                              data[k] = my_iter(v) or v  
                                                                                                                                                                                                                                                                                                                                          elif isinstance(data, (str, bytes)):  
                                                                                                                                                                                                                                                                                                                                                          m = pattern_function.match(data)  
                                                                                                                                                                                                                                                                                                                                                                          if not m:  
                                                                                                                                                                                                                                                                                                                                                                                              m = pattern_function2.match(data)  
                                                                                                                                                                                                                                                                                                                                                                                                              if m:  
                                                                                                                                                                                                                                                                                                                                                                                                                                  return eval(m.group(1))  
                                                                                                                                                                                                                                                                                                                                                                                                                                                  if not m:  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      m = pattern_function3.match(data)  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      if m:  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          K, k = m.group(1).split(':')  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              return bxmat.default_values.get(K).get(k)  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    return data  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  my_iter(data)  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return BXMDict(data)  

可以看到,测试用例还支持一定的模板语法,如${function}$(a:b)等,这能在很大程度上拓展测试人员用例编写的能力

HTTP 请求测试接口

HTTP 请求可以直接用 aioHTTP.ClientSession().request(method,url,**kwargs),HTTP
也是一个协程,可以保证网络请求时不被阻塞,通过await HTTP()便可以拿到接口测试数据。

        async def HTTP(domain, *args, **kwargs):  
                """  
                        HTTP 请求处理器  
                                :param domain: 服务地址  
                                        :param args:  
                                                :param kwargs:  
                                                        :return:  
                                                                """  
                                                                        method, api = args  
                                                                                arguments = kwargs.get('data') or kwargs.get('params') or kwargs.get('json') or {}  
                                                                                      
                                                                                              # kwargs 中加入 token  
                                                                                                      kwargs.setdefault('headers', {}).update({'token': bxmat.token})  
                                                                                                              # 拼接服务地址和 api  
                                                                                                                      url = ''.join([domain, api])  
                                                                              
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值