1. 程式人生 > 實用技巧 >Python3-介面自動化-3-介面自動化專案目錄框架

Python3-介面自動化-3-介面自動化專案目錄框架

一、專案結構

1. 新建一個工程,工程名為:sales_interface_auto

2. 在工程的根目錄新建一個py指令碼:runAll.py 執行介面自動化的入口,專案工程部署完畢後直接執行該檔案即可

3. 在專案下建立幾個package包:

----common:這個包放置一些公共的方法,例如:讀取excel,讀取mysql,get和post請求的封裝,傳送Email的封裝,讀取手機公共引數的封裝,Log.py是封裝日誌的輸入

----config:這個包裡是放置一些獲取根資料夾目錄,介面伺服器地址,讀寫配置檔案封裝的方法

----result: 該包裡存放日誌,截圖,HTML報告

----testCase:這個包放test開頭的測試用例,也可以放一些封裝介面的方法

----testFile/case:存放excel測試用例

----testModels :存放對應介面指令碼的裝飾器提取器

----util/generator:介面測試中用到的一些資料的生成,如:身份證號,名字,手機號,隨機字串等

----caselist.txt :順序存放testCase中test打頭的用例,切記注意順序,無需執行時,首位加#號即可

----config.ini :這裡是配置檔案,如郵箱的一些引數,資料庫,手機靜態引數,以及存放測試過程生成的引數

----config_url.ini:這裡是配置檔案,存放介面地址

二、詳細介紹個目錄

1.runAll.py

原始碼如下:

import os
import common.HTMLTestRunner as HTMLTestRunner
from config import readConfig, getpathInfo
import unittest
from common.configEmail import send_email
import common.Log
from testCase.test_init_user_info import InitUserInfo
from config import readConfig as readConfig, writeConfig as writeConfig, readExcel, geturlParams

writeconfig = writeConfig.WriteConfig()


send_mail = send_email()
path = getpathInfo.get_Path()
report_path = os.path.join(path, 'result')
on_off = readConfig.ReadConfig().get_email('on_off')
loggger = common.Log.logger

#定義一個類AllTest
class AllTest:

    # 初始化一些引數和資料
    def __init__(self):

        global resultPath

        # result/report.html
        resultPath = os.path.join(report_path, "report.html")

        # 配置執行哪些測試檔案的配置檔案路徑
        self.caseListFile = os.path.join(path, "caselist.txt")

        # 真正的測試斷言檔案路徑
        self.caseFile = os.path.join(path, "testCase")

        # 初始化一下測試中用到的資料,並存儲到配置檔案
        self.idno = InitUserInfo().generate_id_no()
        self.c_name = InitUserInfo().generate_c_name()
        self.c_mobile = InitUserInfo().generate_mobile()
        self.caseList = []
        writeconfig.write_potentiall_user_info(self.idno,self.c_name,self.c_mobile)

        loggger.info('resultPath'+resultPath)# 將resultPath的值輸入到日誌,方便定位檢視問題
        loggger.info('caseListFile'+self.caseListFile)
        loggger.info('caseList'+str(self.caseList))


    def set_case_list(self):
        """
        讀取caselist.txt檔案中的用例名稱,並新增到caselist元素組
        :return:
        """
        fb = open(self.caseListFile)
        for value in fb.readlines():
            data = str(value)
            if data != '' and not data.startswith("#"):# 如果data非空且不以#開頭
                self.caseList.append(data.replace("\n", ""))# 讀取每行資料會將換行轉換為\n,去掉每行資料中的\n
        fb.close()

    def set_case_suite(self):
        """

        :return:
        """
        self.set_case_list()# 通過set_case_list()拿到caselist元素組
        test_suite = unittest.TestSuite()
        suite_module = []
        for case in self.caseList:# 從caselist元素組中迴圈取出case
            case_name = case.split("/")[-1]# 通過split函式來將aaa/bbb分割字串,-1取後面,0取前面
            print(case_name+".py")# 打印出取出來的名稱
            # 批量載入用例,第一個引數為用例存放路徑,第一個引數為路徑檔名
            discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)
            suite_module.append(discover)# 將discover存入suite_module元素組
            print('suite_module:'+str(suite_module))
        if len(suite_module) > 0:# 判斷suite_module元素組是否存在元素
            for suite in suite_module:# 如果存在,迴圈取出元素組內容,命名為suite
                for test_name in suite:# 從discover中取出test_name,使用addTest新增到測試集
                    test_suite.addTest(test_name)
        else:
            print('測試套件中無可用的測試用例')
            return None
        return test_suite 

    def run(self):
        """
        run test
        :return:
        """
        try:
            suit = self.set_case_suite()#呼叫set_case_suite獲取test_suite
            print('try')
            print(str(suit))
            if suit is not None:#判斷test_suite是否為空
                print('if-suit')
                fp = open(resultPath, 'wb')# 開啟result/20181108/report.html測試報告檔案,如果不存在就建立
                # 呼叫HTMLTestRunner
                runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description')
                runner.run(suit)
            else:
                print("Have no case to test.")
        except Exception as ex:
            print(str(ex))
            # log.info(str(ex))

        finally:
            print("*********TEST END*********")
            # log.info("*********TEST END*********")
            fp.close()
        # 判斷郵件傳送的開關
        if on_off == 'on':
            send_mail.outlook()
        else:
            print("郵件傳送開關配置關閉,請開啟開關後可正常自動傳送測試報告")
# pythoncom.CoInitialize()
# scheduler = BlockingScheduler()
# scheduler.add_job(AllTest().run, 'cron', day_of_week='1-5', hour=14, minute=59)
# scheduler.start()

if __name__ == '__main__':
    AllTest().run()

1.1__init__ 初始化資料

  # 定義全域性變數
    global resultPath

    # 取到report.html絕對路徑
    resultPath = os.path.join(report_path, "report.html")

    # 拿到caselist.txt的絕對路徑 
    self.caseListFile = os.path.join(path, "caselist.txt")

    # 拿到testCase的絕對路徑
    self.caseFile = os.path.join(path, "testCase")

    # 初始化一下測試中用到的資料,並存儲到配置檔案
    self.idno = InitUserInfo().generate_id_no()
    self.c_name = InitUserInfo().generate_c_name()
    self.c_mobile = InitUserInfo().generate_mobile()
    self.caseList = []
    writeconfig.write_potentiall_user_info(self.idno,self.c_name,self.c_mobile)
  
    # 將resultPath等的值輸入到日誌,方便定位檢視問題
loggger.info('resultPath'+resultPath)
    loggger.info('caseListFile'+self.caseListFile)
    loggger.info('caseList'+str(self.caseList))


1.2 set_case_list :讀取caselist.txt檔案中的用例名稱,並新增到caselist元素組

read()         #一次性讀取文字中全部的內容,以字串的形式返回結果

readline()      #只讀取文字第一行的內容,以字串的形式返回結果

readlines()     #讀取文字所有內容,並且以數列的格式返回結果,一般配合for in使用

# 開啟caselist.txt檔案,
fb = open(self.caseListFile)

for value in fb.readlines():
    data = str(value)
    # 如果data非空且不以#開頭
    if data != '' and not data.startswith("#"):
        # 讀取每行資料會將換行轉換為\n,去掉每行資料中的\n
self.caseList.append(data.replace("\n", ""))
fb.close()

1.3  set_case_suite 設定測試套件


# 通過set_case_list()拿到caselist元素組
self.set_case_list()
test_suite = unittest.TestSuite()
suite_module = []

# 從caselist元素組中迴圈取出case
for case in self.caseList:

    case_name = case 
    # 打印出取出來的名稱
    print(case_name+".py")
    # 批量載入用例,第一個引數self.caseFile為用例存放路徑,第二個引數case_name為路徑檔名
    discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)
    # 將discover存入suite_module元素組
    suite_module.append(discover)

輸出為此模式:找到測試用例檔案中的test開頭的方法
suite_module:[<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<paramunittest.TestLogin_0 testMethod=test01case>]>]>]>]
    print('suite_module:'+str(suite_module))

# 判斷suite_module元素組是否存在元素
if len(suite_module) > 0:
    # 如果存在,迴圈取出元素組內容,命名為suite
    for suite in suite_module:
        # 從discover中取出test_name,使用addTest新增到測試集
        for test_name in suite:
            test_suite.addTest(test_name)
else:
    print('測試套件中無可執行的測試用例')
    return None
return test_suite


1.4 run 執行測試用例套件

try:

    # 呼叫set_case_suite獲取test_suite
    suit = self.set_case_suite()
    # print('try')
    # print(str(suit))

    # 判斷test_suite是否為空
    if suit is not None:
        # print('if-suit')

        # 開啟result/report.html測試報告檔案,如果不存在就建立
        fp = open(resultPath, 'wb')
        # 呼叫HTMLTestRunner
        runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='UU隊長 測試報告', description='測試用例執行結果')

        # 通過HTMLTestRunner的run()方法來執行測試套件中的測試用例,並寫入測試報告
        runner.run(suit)
    else:
        print("Have no case to test.")
except Exception as ex:
    print(str(ex))
    # log.info(str(ex))

finally:
    print("*********TEST END*********")
    # log.info("*********TEST END*********")
    fp.close()
# 判斷郵件傳送的開關
if on_off == 'on':
    send_mail.outlook()
else:
    print("郵件傳送開關配置關閉,請開啟開關後可正常自動傳送測試報告")

2. readConfig.py  讀取配置檔案

程式碼如下:
import os
import configparser
from config import getpathInfo

path = getpathInfo.get_Path()# 專案路徑
config_path = os.path.join(path, 'config.ini')# config.ini的絕對路徑
config_uri_path = os.path.join(path, 'config_uri.ini')# config_uri.ini的絕對路徑
config = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法
config_uri = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法
config.read(config_path, encoding='utf-8')
config_uri.read(config_uri_path, encoding='utf-8')

class ReadConfig():


    def get_http(self, name):

        value = config.get('HTTP', name)
        return value
    def get_email(self, name):

        value = config.get('EMAIL', name)
        return value
    def get_mysql(self, name):
        value = config.get('DATABASE', name)
        return value

    def get_static_params(self,name):

        value = config.get("StaticParams",name)
        return value

    def get_interface_uri(self,name):

        value = config_uri.get("URI",name)

        return value

    def get_common_params(self,name):

        path = getpathInfo.get_Path()  # 呼叫例項化,還記得這個類返回的路徑為
        config_path = os.path.join(path, 'config.ini')  # 這句話是在path路徑下再加一級,最後變成
        config = configparser.ConfigParser()  # 呼叫外部的讀取配置檔案的方法
        config.read(config_path, encoding='utf-8')
        value = config.get("CommonParams",name)
        print(value)
        # logger.info("=========readconfig===="+value)
        return value

    def get_personal_information(self,name):

        value = config.get("PersonalInformation", name)
        # print(value)
        # logger.info("=========readconfig===="+value)
        return value





if __name__ == '__main__':# 測試一下,我們讀取配置檔案的方法是否可用

    print('HTTP中的baseurl值為:', ReadConfig().get_http('baseurl'))
    print('EMAIL中的開關on_off值為:', ReadConfig().get_email('on_off'))
    print('StaticParams中的開關clientIp值為:', ReadConfig().get_static_params('clientIp'))

2.1 靜態呼叫

# 專案路徑
path = getpathInfo.get_Path()

# config.ini的絕對路徑
config_path = os.path.join(path, 'config.ini')
# config_uri.ini的絕對路徑
config_uri_path = os.path.join(path, 'config_uri.ini')
# 呼叫外部的讀取配置檔案的方法
config = configparser.ConfigParser()
config_uri = configparser.ConfigParser()
# utf-8的方式讀取配置檔案
config.read(config_path, encoding='utf-8')
config_uri.read(config_uri_path, encoding='utf-8')

2.2 具體實現

[StaticParams]
clientmac = 9e:ee:fb:0f:5b:b8
clientdensity = 3.0
latitude = 255
longitude = 255
isjailbroken = 0
jailreason = NO Jail
clientversion = 3.2.5
deviceid = 9e:ee:fb:0f:5b:b8
platform = Android
deviceinfo = {"deviceModel":"MI 5","deviceOs":"23_6.0.1"}
network = wifi
screensize = 1920*1080
clientip = 10.0.3.15

讀取程式碼

class ReadConfig():
  # 封裝讀取靜態引數
    def get_static_params(self,name):

        value = config.get("StaticParams",name)
        return value

3. 寫入配置檔案

import os,sys
import configparser
from config import getpathInfo

path = getpathInfo.get_Path()# 呼叫例項化,還記得這個類返回的路徑為
config_path = os.path.join(path, 'config.ini')# 這句話是在path路徑下再加一級,最後變成
config = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法
config.read(config_path, encoding='utf-8')

class WriteConfig():


    def write_common_params(self,token,id,username,secret):

        # print(config.options("CommonParams"))

        config.set("CommonParams",'customer_token',token)
        config.set("CommonParams",'user_id',id)
        config.set("CommonParams",'username',username)
        config.set("CommonParams",'secret',secret)
        # logger.info("======writeconfig======"+token)

        with open(config_path,'w',encoding="utf-8") as f:

            config.write(f)
            # sys.stdout.flush()

if __name__ == '__main__':# 測試一下,我們讀取配置檔案的方法是否可用


     WriteConfig().write_common_params("123","哈哈","1232323")

4. Log.py日誌檔案

程式碼如下:

import os
import logging
from logging.handlers import TimedRotatingFileHandler
from config import getpathInfo

path = getpathInfo.get_Path()
log_path = os.path.join(path, 'result')  # 存放log檔案的路徑


class Logger(object):
    def __init__(self, logger_name='logs…'):
        self.logger = logging.getLogger(logger_name)
        logging.root.setLevel(logging.NOTSET)
        self.log_file_name = 'logs'  # 日誌檔案的名稱
        self.backup_count = 5  # 最多存放日誌的數量
        # 日誌輸出級別
        self.console_output_level = 'WARNING'
        self.file_output_level = 'DEBUG'
        # 日誌輸出格式
        self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def get_logger(self):
        """在logger中新增日誌控制代碼並返回,如果logger已有控制代碼,則直接返回"""
        if not self.logger.handlers:  # 避免重複日誌
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(self.formatter)
            console_handler.setLevel(self.console_output_level)
            self.logger.addHandler(console_handler)

            # 每天重新建立一個日誌檔案,最多保留backup_count份
            file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D',
                                                    interval=1, backupCount=self.backup_count, delay=True,
                                                    encoding='utf-8')
            file_handler.setFormatter(self.formatter)
            file_handler.setLevel(self.file_output_level)
            self.logger.addHandler(file_handler)
        return self.logger


logger = Logger().get_logger()

5. 裝飾器提取響應,類似Java中的getter和setter方法

__author__ = 'csjin'

# 定義@property裝飾器
class Login(object):

    @property
    def code(self):

        return self.__code

    @code.setter
    def code(self,code):

        self.__code = code

    @property
    def msg(self):

        return self.__msg

    @msg.setter
    def msg(self,msg):

        self.__msg = msg

    @property
    def customer_token(self):

        return self.__token

    @customer_token.setter
    def customer_token(self,token):

        self.__token = token