基於Python+Requests+Pytest+YAML+Allure實現介面自動化

wintest發表於2020-08-02

本專案實現介面自動化的技術選型:Python+Requests+Pytest+YAML+Allure ,主要是針對之前開發的一個介面專案來進行學習,通過 Python+Requests 來傳送和處理HTTP協議的請求介面,使用 Pytest 作為測試執行器,使用 YAML 來管理測試資料,使用 Allure 來生成測試報告。

介面專案開發學習:
使用Flask開發簡單介面(1)--GET請求介面
使用Flask開發簡單介面(2)--POST請求介面
使用Flask開發簡單介面(3)--引入MySQL
使用Flask開發簡單介面(4)--藉助Redis實現token驗證
使用Flask開發簡單介面(5)--資料加密處理

專案說明

本專案在實現過程中,把整個專案拆分成請求方法封裝、HTTP介面封裝、關鍵字封裝、測試用例等模組。

首先利用Python把HTTP介面封裝成Python介面,接著把這些Python介面組裝成一個個的關鍵字,再把關鍵字組裝成測試用例,而測試資料則通過YAML檔案進行統一管理,然後再通過Pytest測試執行器來執行這些指令碼,並結合Allure輸出測試報告。

當然,如果感興趣的話,還可以再對介面自動化進行Jenkins持續整合。

GitHub專案原始碼地址:https://github.com/wintests/pytestDemo

專案結構

  • api ====>> 介面封裝層,如封裝HTTP介面為Python介面
  • common ====>> 各種工具類
  • core ====>> requests請求方法封裝、關鍵字返回結果類
  • config ====>> 配置檔案
  • data ====>> 測試資料檔案管理
  • operation ====>> 關鍵字封裝層,如把多個Python介面封裝為關鍵字
  • pytest.ini ====>> pytest配置檔案
  • requirements.txt ====>> 相關依賴包檔案
  • testcases ====>> 測試用例

請求方法封裝

core/rest_client.py 檔案中,對 Requests 庫下一些常見的請求方法進行了簡單封裝,以便呼叫起來更加方便。

class RestClient():

    def __init__(self, api_root_url):
        self.api_root_url = api_root_url
        self.session = requests.session()

    def get(self, url, **kwargs):
        return self.request(url, "GET", **kwargs)

    def post(self, url, data=None, json=None, **kwargs):
        return self.request(url, "POST", data, json, **kwargs)

    def put(self, url, data=None, **kwargs):
        return self.request(url, "PUT", data, **kwargs)

    def delete(self, url, **kwargs):
        return self.request(url, "DELETE", **kwargs)

    def patch(self, url, data=None, **kwargs):
        return self.request(url, "PATCH", data, **kwargs)

    def request(self, url, method, data=None, json=None, **kwargs):
        url = self.api_root_url + url
        headers = dict(**kwargs).get("headers")
        params = dict(**kwargs).get("params")
        files = dict(**kwargs).get("params")
        cookies = dict(**kwargs).get("params")
        self.request_log(url, method, data, json, params, headers, files, cookies)
        if method == "GET":
            return self.session.get(url, **kwargs)
        if method == "POST":
            return requests.post(url, data, json, **kwargs)
        if method == "PUT":
            if json:
                # PUT 和 PATCH 中沒有提供直接使用json引數的方法,因此需要用data來傳入
                data = complexjson.dumps(json)
            return self.session.put(url, data, **kwargs)
        if method == "DELETE":
            return self.session.delete(url, **kwargs)
        if method == "PATCH":
            if json:
                data = complexjson.dumps(json)
            return self.session.patch(url, data, **kwargs)

HTTP介面 封裝為 Python介面

api/user.py 檔案中,將上面封裝好的HTTP介面,再次封裝為不同的Python介面。不同的Python介面,會處理不同URL下的請求。

class User(RestClient):

    def __init__(self, api_root_url, **kwargs):
        super(User, self).__init__(api_root_url, **kwargs)

    def list_all_users(self, **kwargs):
        return self.get("/users", **kwargs)

    def list_one_user(self, username, **kwargs):
        return self.get("/users/{}".format(username), **kwargs)

    def register(self, **kwargs):
        return self.post("/register", **kwargs)

    def login(self, **kwargs):
        return self.post("/login", **kwargs)

    def update(self, user_id, **kwargs):
        return self.put("/update/user/{}".format(user_id), **kwargs)

    def delete(self, name, **kwargs):
        return self.post("/delete/user/{}".format(name), **kwargs)

關鍵字返回結果類

core/result_base.py 下,定義了一個空類 ResultBase ,該類主要用於自定義關鍵字返回結果。

class ResultBase():
    pass

"""
自定義示例:
result = ResultBase()
result.success = False
result.msg = res.json()["msg"]
result.response = res
"""

在多流程的業務場景測試下,通過自定義期望儲存的返回資料值,以便更好的進行斷言。

關鍵字封裝

關鍵字應該是具有一定業務意義的,在封裝關鍵字的時候,可以通過呼叫多個Python介面來完成。在某些情況下,比如測試一個充值介面的時候,在充值後可能需要呼叫查詢介面得到最新賬戶餘額,來判斷查詢結果與預期結果是否一致,那麼可以這樣來進行測試:

  • 1, 首先,可以把 充值-查詢 的操作封裝為一個關鍵字,在這個關鍵字中依次呼叫充值和查詢的介面,並可以自定義關鍵字的返回結果。
  • 2, 接著,在編寫測試用例的時候,直接呼叫關鍵字來進行測試,這時就可以拿到關鍵字返回的結果,那麼斷言的時候,就可以直接對關鍵字返回結果進行斷言。

測試用例層

根據用例名分配測試資料

測試資料位於 data 資料夾下,在這裡使用 YAML 來管理測試資料,同時要求測試資料中第一層的名稱,需要與測試用例的方法名保持一致,如 test_get_all_user_infotest_delete_user

test_get_all_user_info:
  # 期望結果,期望返回碼,期望返回資訊
  # except_result, except_code, except_msg
  - [True, 0, "查詢成功"]
省略
test_delete_user:
  # 刪除的使用者名稱,期望結果,期望返回碼,期望返回資訊
  # username, except_result, except_code, except_msg
  - ["測試test", True, 0, "刪除使用者資訊成功"]
  - ["wintest3", False, 3006, "該使用者不允許刪除"]

這裡藉助 fixture 方法,我們就能夠通過 request.function.__name__ 自動獲取到當前執行用例的函式名 testcase_name ,當我們傳入測試資料 api_data 之後,接著便可以使用 api_data.get(testcase_name) 來獲取到對應用例的測試資料。

import pytest
from testcases.conftest import api_data

@pytest.fixture(scope="function")
def testcase_data(request):
    testcase_name = request.function.__name__
    return api_data.get(testcase_name)

資料準備和清理

在介面自動化中,為了保證用例可穩定、重複地執行,我們還需要有測試前置操作和後置操作,即資料準備和資料清理工作。

@pytest.fixture(scope="function")
def delete_register_user():
    """註冊使用者前,先刪除資料,用例執行之後,再次刪除以清理資料"""
    del_sql = base_data["init_sql"]["delete_register_user"]
    db.execute_db(del_sql)
    logger.info("註冊使用者操作:清理使用者--準備註冊新使用者")
    logger.info("執行前置SQL:{}".format(del_sql))
    yield # 用於喚醒 teardown 操作
    db.execute_db(del_sql)
    logger.info("註冊使用者操作:刪除註冊的使用者")
    logger.info("執行後置SQL:{}".format(del_sql))

在這裡,以使用者註冊用例為例。對於前置操作,我們應該準備一條刪除SQL,用於將資料庫中已存在的相同使用者刪除,對於後置操作,我們應該再執行刪除SQL,確保該測試資料正常完成清理工作。

在測試用例中,我們只需要在用例上傳入 fixture 的函式引數名 delete_register_user ,這樣就可以呼叫 fixture 實現測試前置及後置操作。當然,也可以使用pytest裝飾器 @pytest.mark.usefixtures() 來完成,如:

@pytest.mark.usefixtures("delete_register_user")

Allure用例描述

在這裡,我們結合 Allure 來實現輸出測試報告,同時我們可以使用其裝飾器來新增一些用例描述並顯示到測試報告中,以便報告內容更加清晰、直觀、可讀。如使用 @allure.title() 自定義報告中顯示的用例標題,使用 @allure.description() 自定義用例的描述內容,使用 @allure.step() 可在報告中顯示操作步驟,使用 @allure.issue() 可在報告中顯示缺陷及其連結等。

@allure.step("步驟1 ==>> 註冊使用者")
def step_1(username, password, telephone, sex, address):
    logger.info("步驟1 ==>> 註冊使用者 ==>> {}, {}, {}, {}, {}".format(username, password, telephone, sex, address))

@allure.severity(allure.severity_level.NORMAL)
@allure.epic("針對單個介面的測試")
@allure.feature("使用者註冊模組")
class TestUserRegister():
    """使用者註冊"""
    @allure.story("用例--註冊使用者資訊")
    @allure.description("該用例是針對獲取使用者註冊介面的測試")
    @allure.issue("https://www.cnblogs.com/wintest", name="點選,跳轉到對應BUG的連結地址")
    @allure.testcase("https://www.cnblogs.com/wintest", name="點選,跳轉到對應用例的連結地址")
    @allure.title(
        "測試資料:【 {username},{password},{telephone},{sex},{address},{except_result},{except_code},{except_msg}】")
    @pytest.mark.single
    @pytest.mark.parametrize("username, password, telephone, sex, address, except_result, except_code, except_msg",
                             api_data["test_register_user"])
    @pytest.mark.usefixtures("delete_register_user")
    def test_delete_user(self, login_fixture, username, except_result, except_code, except_msg):
省略

專案部署

首先,下載專案原始碼後,在根目錄下找到 requirements.txt 檔案,然後通過 pip 工具安裝 requirements.txt 依賴,執行命令:

pip3 install -r requirements.txt

接著,修改 config/setting.ini 配置檔案,在Windows環境下,安裝相應依賴之後,在命令列視窗執行命令:

pytest

注意:因為我這裡是針對自己的介面專案進行測試,如果想直接執行我的測試用例來檢視效果,需要提前部署上面提到的介面專案。

測試報告效果展示

在命令列執行命令:pytest 執行用例後,會得到一個測試報告的原始檔案,但這個時候還不能開啟成HTML的報告,還需要在專案根目錄下,執行命令啟動 allure 服務:

# 需要提前配置allure環境,才可以直接使用命令列
allure serve ./report

最終,可以看到測試報告的效果圖如下:

image.png

相關文章