美文网首页python进阶课自动化测试笔记
从零搭建接口测试框架(六)——测试与测试报告

从零搭建接口测试框架(六)——测试与测试报告

作者: 小楼_987f | 来源:发表于2019-05-17 19:07 被阅读28次

    六. pytest参数化

    这一小节我们用 pytest 来断言测试用例并用 pytest-html 生成测试报告。

    1. 接口加密

    encryption.py 中定义了两个函数 MD5_signencryptAES,调用前者会返回一个数字签名;后者会对传入的数据使用AES算法进行加密,返回加密数据用于传输。
    接口的解密已经在<Django接口开发>小节中有过叙述,这里就不重复了,有兴趣的话可以跳回去了解。
    encryption.py 的具体内容如下:

      1 import time
      2 import hashlib
      3 from Crypto.Cipher import AES
      4 import base64
      5 
      6 
      7 def MD5_sign():
      8     """
      9     生成MD5数字签名并返回
     10     """
     11     key = "wahaha"
     12     now_time = time.time()      # 获取当前时间
     13     client_time = str(now_time).split('.')[0]  # 转换为字符串,截取小数点前
     14     
     15     # 生成MD5签名
     16     hash = hashlib.md5()
     17     sign = client_time + key
     18     sign_utf8 = sign.encode(encoding='utf-8')
     19     hash.update(sign_utf8)
     20     sign_md5 = hash.hexdigest()
     21     md5_sign = "%s|%s" %(sign_md5, client_time)
     22 
     23     return md5_sign
     24 
     25     
     26 def encryptAES(src):
     27     """
     28     AES加密函数
     29     """
     30     key = "qwertyuiopasdfgh"
     31     iv = b"1234567890123456"    # 初始化向量
     32     block = 16  # 密码块要求16位,该数据用于计算填充补位
     33     aes = AES.new(key, AES.MODE_CBC, iv)   # 初始化加密器
     34     # 如果s不足16位,进行填充。填充模式:PKCS#5/PKCS7
     35     pad = lambda s: s + (block-len(s)%block) * chr(block-len(s)%block)
     36     src = aes.encrypt(pad(src))    # 进行AES加密
     37     return base64.urlsafe_b64encode(src)    # 二次转换,便于传输
    

    2. 测试

    pytest 框架可以轻松编写小型测试,然后进行扩展以支持应用程序和库的复杂功能测试,并且支持参数化功能而不必像 unittest 那样再去引入专门的参数化模块。
    安装pytest:pip install pytest
    test_main.py:

      1 import os
      2 import hashlib
      3 import json
      4 import sys
      5 import pytest
      6 import requests
      7 import getCase
      8 import encryption
      9 
     10 
     11 # 获取测试数据
     12 excel = getCase.getCase().get_xls()
     13 
     14 class TestApi(object):
     15     # 装饰器,实现参数化
     16     @pytest.mark.parametrize('num, api_name, description, api_host, request_url, request_method, request_data, encryption
    _method, check_point, active', excel)
     17     # 测试用例
     18     def test_api(self, num, api_name, description, api_host, request_url, request_method, request_data, encryption_method, check_point, active):
     19         # 拼接出完整请求地址
     20         url = api_host.replace('\n', '').replace('\r', '') + request_url
     21         # 以防万一,如果用例未激活则跳过
     22         if active == "no":
     23             pytest.skip("active为no,跳过该测试用例")
     24         elif active == "yes":
     25             # 处理GET请求
     26             if  request_method == "GET":
     27                 # 如果请求需要MD5签名
     28                 if encryption_method == 'MD5':
     29                     data = json.loads(request_data)
     30                     sign = encryption.MD5_sign()
     31                     data.update(md5_sign=sign)
     32                     session = requests.Session()
     33                     # 禁止代理服务
     34                     session.trust_env = False
     35                     r = session.get(url, params=data)
     36                 else:
     37                     session = requests.Session()
     38                     session.trust_env = False
     39                     r = session.get(url, params=request_data)
     40 
     41             # 处理POST请求
     42             elif request_method  == "POST":
     43                 data = json.loads(request_data)
     44                 session = requests.Session()
     45                 session.trust_env = False
     46                 # AES加密处理
     47                 if encryption
    _method == 'AES':
     48                     encoded = encryption.encryptAES(json.dumps(data)).decode()
     49                     r = session.post(url, data={'data': encoded})
     50                 # 未加密请求
     51                 elif encryption_method == 'no':
     52                     r = session.post(url, data=data)
     53 
     54             # result保存响应值
     55             result = r.json()
     56             # 检查
     57             assert result['status'] == int(check_point.split(':', 1)[0])
     58             assert result['message'] == check_point.split(':', 1)[1]
    

    现在终于可以运行一下测试用例啦:

    >>> pytest -q test_main.py 
    .......                                                                                                   [100%]
    =============================================== warnings summary ================================================
    /usr/lib/python3.7/site-packages/requests/__init__.py:91
      /usr/lib/python3.7/site-packages/requests/__init__.py:91: RequestsDependencyWarning: urllib3 (1.25.2) or chardet (3.0.4) doesn't match a supported version!
        RequestsDependencyWarning)
    
    -- Docs: https://docs.pytest.org/en/latest/warnings.html
    7 passed, 1 warnings in 0.37 seconds
    

    需要注意的是,这份测试用例是不严谨的。作为一份Demo,它仅搭建了一个演示架构,内部填充需要根据产品的实际情况做出调整。

    3. 测试报告

    pytest-html 是pytest的一个插件,它为测试结果生成一个HTML报告。
    安装:pip install pytest-html
    为了遵守内容安全策略(CSP),默认情况下,pytest --html=report.html命令生成的测试报告会单独存储CSS和图像等多个资源。您也可以创建一个独立的报告,在共享结果时更方便。这可以通过以下命令完成:
    pytest --html = report.html --self-contained-html
    现在我们试着生成一份独立的测试报告:
    >>> pytest -q ./test_main.py --html=../Report/haha.html --self-contained-html
    通过上面这条命令我们在 IntTestDemo/Report/ 目录下生成了一份名为 haha.html 的测试报告,可以用浏览器打开看一下:

    测试报告.png 这样的报告显然是不符合要求的,一来中文没能显示,二来格式也不够清晰,所以我们还需要定制一下测试报告,官网上有相关例子可以作为参考。
    App 目录下新建一个文件 conftest.py ,内容如下:
      1 from datetime import datetime
      2 from py.xml import html
      3 import pytest
      4 
      5 @pytest.mark.optionalhook
      6 def pytest_html_results_table_header(cells):
      7     #cells.insert(1, html.th('Time', class_='sortable time', col='time'))
      8     cells.insert(3, html.th('Params'))
      9     cells.insert(2, html.th('Description'))
     10     cells.insert(0, html.th('No.'))
     11     cells.pop()
     12 
     13 @pytest.mark.optionalhook
     14 def pytest_html_results_table_row(report, cells):
     15     #cells.insert(1, html.td(datetime.utcnow(), class_='col-time'))
     16     cells.insert(3, html.td(report.params))
     17     cells.insert(2, html.td(report.description))
     18     cells.insert(0, html.td(report.number))
     19     cells.pop()
     20 
     21 @pytest.mark.hookwrapper
     22 def pytest_runtest_makereport(item, call):
     23     outcome = yield
     24     report = outcome.get_result()
     25     # 对test一列重新编码,显示中文
     26     report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")
     27     # test列中包含了测试用例传入的所以参数,先对其进行分割
     28     result = report.nodeid.split('-')
     29     # 从test列剥离出不同的参数信息
     30     number = result[0].split('[')[1]
     31     api_name = result[1]
     32     description = result[2]
     33     url = result[4]
     34     request_method = result[5]
     35     params = result[6]
     36     check_point = result[8]
     37     # 对新插入的表格进行赋予参数值
     38     report.nodeid = api_name + ' -- ' + request_method + ' -- ' + url
     39     report.description = str(description)
     40     report.number = str(number)
     41     report.url = str(url)
     42     report.params = str(params)
    

    再次生成测试报告看看:
    >>> pytest -q ./test_main.py --html=../Report/haha.html --self-contained-html

    定制后的测试报告.png

    4. 又是愉快的一天

    >>> cd ../    # 回到根目录IntTestDemo
    >>> git add .    # 将项目改动放入暂存区
    >>> git commit -m "add test_main.py and we can genarate test report now"    # 将暂存区的的修改提交到当前分支,m参数表示添加注释
    >>> git push origin master    # 推送到远程服务器(github)
    

    相关文章

      网友评论

        本文标题:从零搭建接口测试框架(六)——测试与测试报告

        本文链接:https://www.haomeiwen.com/subject/kgxgnqtx.html