pytest+yaml+allure接口自動化測試框架06.接口上下文參數關聯


前言

前面我們已經完成了測試框架的主要功能了,讀取用例,執行用例,獲取結果。在這個請求中間呢,我們沒有解決一個接口測試中很常見的問題,接口上下文參數傳遞,這個是什么意思呢。

比如我們可以用登錄和登錄驗證這兩個接口來講一下,現在常用的系統都是前后端分離的,認證也是通過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。有時間我會盡快更新的。如有筆誤(失誤)請及時指出,謝謝支持!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM