介面自動化實戰之框架搭建

weixin_44885027發表於2020-12-13

新建專案及python包

建立一個新的python專案,在下面建一個run.py檔案作為unittest的TestRunner來跑測試用例;再新建一個tests包放測試用例:
在這裡插入圖片描述
新建config包來存放配置檔案:
在這裡插入圖片描述
新建libs包存放第三方模組:
在這裡插入圖片描述
新建reports包存放執行測試用例後生成的測試報告:
在這裡插入圖片描述
新建data包存放呼叫介面和斷言時所需要準備的測試資料:
在這裡插入圖片描述
新建common包存放公共模組:
在這裡插入圖片描述
新建logs包存放日誌:
在這裡插入圖片描述

跑通用例

接著在tests包下新建一個test_demo.py,先不寫任何實質上的內容,僅僅是個為了測試的空殼。

  • test_demo.py
import unittest

class Test_demo(unittest.TestCase):
    #先什麼都不寫,pass當做這個用例100%能執行通過!
    def test_demo(self):
        pass

接著我們就開始寫run.py

  • run.py
import unittest
import os
from datetime import datetime
from config import config
from libs.HTMLTestRunnerNew import HTMLTestRunner
#載入用例
loader = unittest.TestLoader()
#自動檢索所有測試用例
"""
問題1:路徑的重用
這裡我們準備傳入測試用例所在路徑:tests,直接在這裡用變數定義出來自然是最簡單的,
但後面可能還需要更多需要傳入路徑的場景,所以我們需要找個地方配置所有的路徑。而一
想到配置,我們就會想到放到config目錄下。但具體我們應該用什麼配置呢?yaml嗎?但
yaml只適合配置靜態的配置,路徑是會隨著專案的轉移而發生變化,所以我們可以用一個py
檔案來配置所有的路徑資訊。在config.py定義了測試用例的路徑後,我們可以直接import
過來。
@路徑配置詳見config.py
"""
cases = loader.discover(config.CASES_PATH)

"""
問題2:報告時間戳格式的生成
這裡我們需要定義報告的名稱。而每次生成的報告如果以同一檔名(如:report.html)命名
的話,就會覆蓋之前的報告內容。當我們需要找回歷史報告(如上個月的報告)時就找不到了,所以
我們需要用時間戳格式命名報告的檔名,這樣不僅執行時間可以一目瞭然,還能找到對應時間的
報告回顧。
"""
#定義時間戳
time_format = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
#測試報告檔名稱
report_name = "report-{}.html".format(time_format)
#測試報告路徑
report_path = os.path.join(config.REPORT_PATH,report_name)
#執行測試用例
with open(report_path,mode='wb') as f:
    #這裡我們用第三方模組:HTMLTestRunnerNew來生成測試報告
    runner = HTMLTestRunner(
        f,
        title = "介面自動化測試報告",
        description = "測試報告",
        tester = "tom"
    )
    #執行測試用例
    runner.run(cases)

問題1中我們需要把專案中的所有包的路徑都存放到py檔案中,我們需要在包裡面新建一個config.py:
在這裡插入圖片描述

  • config.py
import os

#配置檔案路徑
CONFIG_PATH = os.path.dirname(os.path.abspath(__file__))

#專案路徑
ROOT_PATH = os.path.dirname(CONFIG_PATH)

#測試用例路徑
CASES_PATH = os.path.join(ROOT_PATH,"tests")

#測試報告路徑
REPORT_PATH = os.path.join(ROOT_PATH,"reports")

在libs包下存放剛剛用到的第三方模組HTMLTestRunnerNew.py:
在這裡插入圖片描述
各個包、TestCase、TestRunner都準備好後,接下來我們去到run.py執行測tests下的所有測試用例,執行結果如下,表明unittest中的TestCase和TestRunner是能跑通的:
在這裡插入圖片描述
而如果執行時報了下面的錯,則表明測試用例是有問題的,要麼是缺少__init__.py檔案,要麼是測試用例命名不規範(注意,命名最好以test開頭),要麼是測試用例內部的程式碼出了問題:
在這裡插入圖片描述

通用模組的構建

我們需要把之前講到的各個測試框架的元件加到common中,包括:excel_handler、logging_handler、yaml_handler、request_handler、sql_handler:
在這裡插入圖片描述

excel_handler

import openpyxl
import pprint

class excel_handler():
    """初始化例項引數"""
    def __init__(self,filepath):
        self.filepath = filepath
        self.workbook = None

    """定義open_file方法,返回workbook物件"""
    def open_file(self):
        workbook = openpyxl.load_workbook(self.filepath)
        self.workbook = workbook
        return workbook

    """通過workbook物件獲取sheet物件"""
    def get_sheet(self,name):
        workbook = self.open_file()
        sheet = workbook[name]
        return sheet

    """獲取表單裡的每一個單元格物件的value"""
    def get_data(self,name):
        #獲取表單物件
        sheet = self.get_sheet(name)
        #獲取表單物件中的所有行
        rows = list(sheet.rows)
        #定義一個列表,用來接收每一行的值
        data = []
        #定義一個列表,獲取所有的標題
        tittles = []
        #遍歷第一行rows[0],獲取所有行的value屬性,放入tittles中
        for tittle in rows[0]:
            tittles.append(tittle.value)
        #遍歷第二行後的所有資料(排除掉第一行是因為已經遍歷完了第一行的標題)
        for row in rows[1:]:
            #定義一個字典,存放每一行的值,每一個值是一個鍵值對,key是標題,值是該行對應的value屬性。
            row_data = {}
            #遍歷拿到的每一行,獲取每一行的每一個單元格放入row_data中.其中key為標題,value為該單元格對應的值.
            for index,cell in enumerate(row):
                row_data[tittles[index]] = cell.value
            #把每一行的資料放入data中
            data.append(row_data)
        return data

    """對單元格的value屬性賦值,寫入到excel中"""
    def write(self, sheet_name, row, column, data):
        sheet = self.get_sheet(sheet_name)
        sheet.cell(row, column).value = data
        self.save()
        self.close()

    """對excel做修改後,儲存excel檔案"""
    def save(self):
        self.workbook.save(self.file_path)

    """關閉workbook物件,釋放記憶體"""
    def close(self):
        self.workbook.close()

logging_handler

import logging

def log_handler(
        logger_name = "logger",
        logger_level = "DEBUG",
        stream_level = "DEBUG",
        file_name = "None",
        file_level = "DEBUG",
        fmt = '%(asctime)s--%(filename)s--No:%(lineno)d--%(levelname)s:%(message)s',

):

    #初始化日誌收集器
    logger = logging.getLogger(logger_name)
    #設定日誌收集器的日誌級別
    logger.setLevel(logger_level)
    #初始化流處理器
    stream_handler = logging.StreamHandler()
    #設定流處理器的日誌級別
    stream_handler.setLevel(stream_level)
    #將流處理器與日誌收集器繫結起來
    logger.addHandler(stream_handler)
    #設定格式
    fmt = logging.Formatter(fmt)
    #設定流處理器的日誌輸出格式
    stream_handler.setFormatter(fmt)
    #如果傳入的file_name不為空,則初始化檔案處理器
    if file_name:
        #初始化檔案處理器
        file_handler = logging.FileHandler(file_name,encoding='UTF-8')
        #設定檔案處理器的日誌級別
        file_handler.setLevel(file_level)
        #將檔案處理器與日誌收集器繫結起來
        logger.addHandler(file_handler)
        #設定檔案處理器的日誌輸出格式
        file_handler.setFormatter(fmt)
    return logger

yaml_handler

import yaml

def read_yaml(file):
    """讀取 yaml 檔案"""
    with open(file, encoding='utf8') as f:
        conf = yaml.load(f, Loader=yaml.SafeLoader)
    return conf


def write_yaml(file, data):
    with open(file, 'w', encoding='utf8') as f:
        yaml.dump(data, f)

request_handler

import requests
import logging

def requests_handler(
#準備請求介面時所需要的引數:請求地址、請求方法、請求頭、url引數、form引數、json引數
        url,
        method = "get",
        headers = None,
        params = None,
        data = None,
        json = None,
):
    """由於requests裡面封裝的各種請求方法(get、post、put、delete、head等)最後都要呼叫request方法,所以
    可以直接呼叫requests完成所有請求方法的呼叫,而不用再一個個請求方法封裝"""
    req = requests.request(method,
                           url,
                           data = data,
                           json = json,
                           params = params,
                           headers = headers
                        )
    try:
        #由於介面最後返回的都是json格式的資料,所以這裡呼叫json方法返回json格式資料
        return req.json()
    except Exception as e:
        #如果呼叫json方法報錯,則說明返回格式不是json格式,這時應該拋異常
        logging.error("返回的格式不是json格式{}".format(e))
        return None

sql_handler

import pymysql
from pymysql.cursors import DictCursor

class mysql_handler():
    #初始化,準備需要建立資料庫連線的所有引數
    def __init__(
            self,
            host = None,
            port = 3306,
            user = None,
            password = None,
            charset="utf8",
            cursorclass=DictCursor
    ):
       #建立資料庫連線,傳入準備好的引數
       self.con = pymysql.connect(
           host=host,
           port=port,
           user=user,
           password=password,
           charset=charset,
           cursorclass=cursorclass
       )
       self.cursor = self.con.cursor()
    #查詢方法,傳入需要執行查詢的sql,以及控制是選擇fetchall還是fetchone的引數is_one
    def query(self,sql,is_one=True):
        #呼叫遊標物件的execute方法執行查詢的sql
        self.cursor.execute(sql)
        #如果is_one為true,則只返回結果中的第一條記錄
        if is_one:
            return self.cursor.fetchone()
        #如果is_one為false,則返回結果中的所有記錄
        return self.cursor.fetchall()
    #關閉遊標物件及連線物件
    def close(self):
        self.cursor.close()
        self.con.close()

相關文章