pytest+yaml+allure接口自动化测试框架06.接口上下文参数关联

时间:2024-03-04 07:16:09

前言

前面我们已经完成了测试框架的主要功能了,读取用例,执行用例,获取结果。在这个请求中间呢,我们没有解决一个接口测试中很常见的问题,接口上下文参数传递,这个是什么意思呢。

比如我们可以用登录和登录验证这两个接口来讲一下,现在常用的系统都是前后端分离的,认证也是通过JWT的方式来搞定的,那么在登录接口进行登录之后就会生成一个token,我们拿到这个token就可以去其他接口进行鉴权,然后才能得到登录验证接口返回值。

所以我们这一章就解决一下这个请求参数上下文传递。

获取token

先梳理一下思路,我们第一个请求的接口是登录接口,它会给我们返回token值,然后传到下一个接口中。所以我们按照执行顺序,先解决拿到返回值这一步。

在yaml文件中我们定义了一个字段Extract,这个字段就是预设一下我们要拿到哪一个值,你得告诉你的程序要那个他才能执行,在这个项目中我们想拿到的就是data这个。

  test_login:
    description: "登录"
    method: post
    route: /weakPwdLogin/?from=web_login
    RequestData:
      data:
        loginName: 18291900215
        password: dd636482aca022
        code:
        description: encrypt
    Validate:
      expectcode: 200
      resultcheck: \'"result":"success"\'
      regularcheck: \'[\d]{16}\'
    Extract:   ---> 注意这一行
      - data

然后我们继续打开common/result.py这个文件,创建一个函数get_result,获取一下请求值。

def get_result(r, extract):
    """获取值"""
    for key in extract:
        value = get_var(key, r.text)
        logger.debug("正则提取结果值:{}={}".format(key, value))
        cache.set(key, value)
        pytest.assume(key in cache)

这个函数的主要工作就是,通过正则表达式获取到结果,然后把他放入到缓存中去。

更新response_handle

创建好之后,我们就需要去我们处理请求得地方把这个函数,给他嵌套进去。

打开conftest.py文件。

from common.result import get_result, check_results
    
    +++
    
    
    def response_handle(self, r: Response, validate: t.Dict, extract: t.List):
        """Handling of responses"""
        if validate:
            check_results(r, validate)
        if extract:
            get_result(r, extract)

好了到这一步,我们的获取token(data)的工作就完成了。

接下来我们要处理的是传入到下一个接口中。

打开YAML测试文件,我们找到测试验证这条用例。我们会发现有一个${data},这是我们定义的一种变量格式。通过识别变量名称,去替换相应的结果。

  test_login_verify:
    description: "验证登录"
    method: post
    route: /loginSuccess/
    RequestData:
      data:
        userId: "${data}"   ---> 这行
    Validate:
      expectcode: 200
      regularcheck:
      resultcheck: \'"result":"success"\'

进行替换

我们首先得封装两个方法,一个方法让我们可以获取到这个用例里面有哪些我们需要替换的变量,一个方法可以让我们执行这个替换的过程。

打开common/regular.py.

from string import Template
from common.cache import cache

+++

def findalls(string):
    """查找所有"""
    key = re.compile(r"\${(.*?)\}").findall(string)
    res = {k: cache.get(k) for k in key}
    logger.debug("需要替换的变量:{}".format(res))
    return res


def sub_var(keys, string):
    """替换变量"""
    s = Template(string)
    res = s.safe_substitute(keys)
    logger.debug("替换结果:{}".format(res))
    return res

  • findalls

    我们通过正则去查找这个用例下有那些变量需要我们去替换。同时把需要替换的变量和变量值,以字典的形式进行存储。

  • sub_var

    通过python官方的string模块中的Template方法,我们可以轻松完成替换,因为我们的变量格式和该模块中的保持了一致。

编写好之后,我们打开common/request.py模块。

from common.json import json, loads, dumps

+++
    
class HttpRequest(Session):
    """requests方法二次封装"""

    def __init__(self, *args, **kwargs):
        super(HttpRequest, self).__init__()
        self.exception = kwargs.get("exception", Exception)

    def send_request(self, **kwargs):
        try:
            +++
    		logger.info("Request Url: {}".format(url))
            logger.info("Request Method: {}".format(method))
            kwargs_str = dumps(kwargs)
            if is_sub := findalls(kwargs_str):
                kwargs = loads(sub_var(is_sub, kwargs_str))
            logger.info("Request Data: {}".format(kwargs))
            request_data = HttpRequest.mergedict(kwargs.get(\'RequestData\'),
                                                 headers=cache.get(\'headers\'),
                                                 timeout=cache.get(\'timeout\'))
            +++
    +++

我们对send_request方法进行改造,在这里我们就用到了我们上一章编写的序列化和反序列化方法。

我们先把请求的dict数据,通过反序列化转换为json字符串。传给findalls方法获取到我们需要替换的变量。然后在调用我们编写的sub_var进行字符串的模板替换,生成新的json字符串,然后在通过序列化方法转换为dict数据,传给requests进行请求,这样我们就实现了,接口的上下文参数传递。是不是非常简单呢。

在完成以上操作后我们可以执行一下看看。

(env) > pytest
================================================================= test session starts =================================================================
platform win32 -- Python 3.8.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0   
rootdir: D:\VScode\Interface_test_example, configfile: pytest.ini       
plugins: assume-2.4.3, html-3.1.1, metadata-1.11.0
collecting ... 
----------------------------------------------------------------- live log collection ----------------------------------------------------------------- 
DEBUG 22:33:59 [regular.py:19] 11052 需要替换的变量:{}
DEBUG 22:33:59 [regular.py:27] 11052 替换结果:{"baseurl": "https://www.zhixue.com", "timeout": 30.0, "headers": {"Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "keep-alive", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", "cookies": "aliyungf_tc=AQAAANNdlkvZ2QYAIb2Q221oiyiSOfhg; tlsysSessionId=cf0a8657-4a02-4a40-8530-ca54889da838; isJump=true; deviceId=27763EA6-04F9-4269-A2D5-59BA0FB1F154; 6e12c6a9-fda1-41b3-82ec-cc496762788d=webim-visitor-69CJM3RYGHMP79F7XV2M; loginUserName=18291900215", "X-Requested-With": "XMLHttpRequest"}}
collected 2 items                                                                                                                                       

tests/testcase.yaml::\u767b\u5f55
-------------------------------------------------------------------- live log call -------------------------------------------------------------------- 
INFO 22:33:59 [request.py:51] 11052 request data: {\'description\': \'登录\', \'method\': \'post\', \'route\': \'/weakPwdLogin/?from=web_login\', \'RequestData\': {\'data\': {\'loginName\': 18291900215, \'password\': \'dd636482aca022\', \'code\': None, \'description\': \'encrypt\'}}, \'Validate\': {\'expectcode\': 200, \'resultcheck\': 
\'"result":"success"\', \'regularcheck\': \'[\\d]{16}\'}, \'Extract\': [\'data\']}
INFO 22:33:59 [request.py:54] 11052 Request Url: https://www.zhixue.com/weakPwdLogin/?from=web_login
INFO 22:33:59 [request.py:55] 11052 Request Method: POST
DEBUG 22:33:59 [regular.py:19] 11052 需要替换的变量:{}
INFO 22:33:59 [request.py:59] 11052 Request Data: {\'description\': \'登录\', \'method\': \'post\', \'route\': \'/weakPwdLogin/?from=web_login\', \'RequestData\': {\'data\': {\'loginName\': 18291900215, \'password\': \'dd636482aca022\', \'code\': None, \'description\': \'encrypt\'}}, \'Validate\': {\'expectcode\': 200, \'resultcheck\': 
\'"result":"success"\', \'regularcheck\': \'[\\d]{16}\'}, \'Extract\': [\'data\']}
INFO 22:34:00 [request.py:73] 11052 Request Result: <Response [200]>{"data":"1500000100070008427","result":"success"}
DEBUG 22:34:01 [result.py:21] 11052 正则提取结果值:data=1500000100070008427                                                                                                      
INFO 22:34:01 [request.py:51] 11052 request data: {\'description\': \'验证登录\', \'method\': \'post\', \'route\': \'/loginSuccess/\', \'RequestData\': {\'data\': {\'userId\': \'${data}\'}}, \'Validate\': {\'expectcode\': 200, \'regularcheck\': None, \'resultcheck\': \'"result":"success"\'}}
INFO 22:34:01 [request.py:54] 11052 Request Url: https://www.zhixue.com/loginSuccess/
INFO 22:34:01 [request.py:55] 11052 Request Method: POST
DEBUG 22:34:01 [regular.py:19] 11052 需要替换的变量:{\'data\': \'1500000100070008427\'}
DEBUG 22:34:01 [regular.py:27] 11052 替换结果:{"description": "\u9a8c\u8bc1\u767b\u5f55", "method": "post", "route": "/loginSuccess/", "RequestData": {"data": {"userId": "1500000100070008427"}}, "Validate": {"expectcode": 200, "regularcheck": null, "resultcheck": "\"result\":\"success\""}}
INFO 22:34:01 [request.py:59] 11052 Request Data: {\'description\': \'验证登录\', \'method\': \'post\', \'route\': \'/loginSuccess/\', \'RequestData\': {\'data\': {\'userId\': \'1500000100070008427\'}}, \'Validate\': {\'expectcode\': 200, \'regularcheck\': None, \'resultcheck\': \'"result":"success"\'}}
INFO 22:34:01 [request.py:73] 11052 Request Result: <Response [200]>{"result":"success"}
PASSED                                                                                                                                           [100%] 

可以看到执行成功了,经历了这么多我们才算勉强完成了一个简单的接口自动化测试框架。

后记

后面三章是报告和邮件和集成Jenkins。有时间我会尽快更新的。如有笔误(失误)请及时指出,谢谢支持!