Python3+Selenium2完整的自動化測試實現之旅(七):完整的輕量級自動化框架實現


一、前言

        前面系列Python3+Selenium2自動化系列博文,陸陸續續總結了自動化環境最基礎環境的搭建、IE和Chrome瀏覽器驅動配置、selenium下的webdriver模塊提供的元素定位和操作鼠標、鍵盤、警示框、瀏覽器cookie、多窗口切換等場景的方法、web自動化測試框架、python面向對象和POM設計模型以及python下的單元測試模塊unittest模塊。本來計划陸續循序漸進的繼續寫一些篇章總結python面向對象編程、python下的logging日志模塊、os.path路徑處理模塊、time模塊處理時間,如格式化時間輸出以及一些第三方模塊如讀取和向excel文件中寫入數據的模塊:xlrd和xlwt,寫完這些后,再開始寫本篇的終極目標:編寫一個輕量級的自動化測試框架。最終放棄這樣做,原因:樓主發現python面向對象編程、python的這些標准庫模塊或者第三方模塊其實很多博客已經做了很好的總結,總之,會學習的人,百度后總能從一大堆的文章中,查看並甄選出對自己解決問題或者思考有幫助和收獲的文章,如百度python logging模塊使用,多看博文就一定能找到對自己有幫助的文章。曾經樓主也是在學習實踐中遇到很多坑,也是根據IDE輸出上提示的錯誤自己先思考解決,還是不行就百度一下或者看書,並深度學習下這塊的內容,然后再去解決問題,從這些博文以及自己買的python類的書籍中也是受益良多,這也是樓主一直以來自己的學習方式。這里,每個人都有自己的學習和思考問題的方式,找准適合自己的學習方式並執行它,完成一個階段目標,然后設置下一個新目標,並為之努力。因此,在寫這個輕量級的自動化web測試框架前,我跳過了上述諸多內容,包括且不限於:python面向對象編程、python常用標准庫loggging、time、os.path運用等等,在后面的輕量級框架代碼中會有部分注釋,對於這些python相關的內容學習,大家根據自己的情況去充實,堅持學習並持之以恆。在樓主身邊,有太多類似的人學習總是三天曬魚、兩天打網的,完全沉不下心來學習東西,淺嘗輒止,沒有積淀,如果認定一個東西就去想辦法搞定,加油!樓主也在為自己新的目標fighting中,當然目標是廣義的,可以是生活方面、工作方面、情感方面.......,好像跑偏題了O(∩_∩)O,這些人生雞湯似的廢話就不說了,看看下面這個web自動化測試框架是如何實現的吧~

 二、項目架構說明

       該項目架構基於樓主公司的一款B/S軟件設計,大家也可以根據自己的被測軟件來構建適合自己的架構層級,當然也可以參考樓主的。做自動化測試項目,當搞懂了思想和方法,其實都是萬變不離其宗,就跟寫代碼一樣,語言萬千種,唯一不變的就是語言中殊途同歸的思想,因此,玩會了套路自然就能凌駕於套路之上,運用並加入自己的東西。在PyCharm中新建如下的項目層級:

       有過開發經歷的小伙伴都知道,有個好的交互式開發工具對於我們創建和管理清晰的項目架構很方便,PyCharm就是一款交互良好的python開發工具。樓主上面的項目層級中部分目錄和目錄下的文件沒展開,下面顯示一個完整的目錄結構,並說明每個目錄是用來干嘛?放什么東西?

        當然這個項目層級設計,不是樓主一時間就固定下來的,也是在不斷的摸索和采坑中,不斷調整出的一個適合自己的框架目錄層級。項目框架設計好了后,接下來就是慢慢補充內容一步步實現上面每個目錄需要的東西。來吧,開始造輪子~

 三、配置文件設計

       首先,對於上面的config.ini的配置文件進行配置。說到配置文件,不管是開發人員還是測試人員都不會陌生,還有xml、txt等格式的配置文件,配置文件就是用來配置一些參數和固定的變量值,一般是程序固定不變的東西我們就放這里面,用於程序直接調用,如果修改配置文件中變量的值,程序調用該變量就會產生不同的輸出行為。如在做自動化測試時,我們可以將測試的不同瀏覽器寫入到該文件中,當我們需要調用firefox瀏覽器時,將參數設置成firefox即可,測試腳本將會在火狐瀏覽器進行。如下圖,在配置文件中設置了瀏覽器參數、測試url、郵件服務器、郵件發送和接收者等參數。

至於為什么這個ini配置文件需要編輯成【###】然后下面是參數或者變量的賦值,自己百度學習:ini配置文件格式,看下是怎么編輯的?都有哪些要素?

、日志類模塊的實現

       說到日志,大家都明白日志的作用,最明顯的作用就是在程序的關鍵位置或者步驟節點下設置日志輸出,當程序運行時,會輸出日志,日志是我們查看程序運行情況和查找錯誤的重要手段。因此,對於自動化測試也是如此,我們需要知道自動化執行的情況以及執行錯誤的情況發生了什么,那就需要給你的自動化測試項目封裝一個日志類的功能模塊,用於輸出日志。python語言封裝了一個叫logging的標准庫模塊,能夠設置日志等級以及怎么輸出、輸出到哪里。對於logging模塊,大家可以自己針對性去學習該模塊的使用。

       我們在上面的項目層級的models目錄下創建log.py文件,在這個模塊文件下定義一個叫做Logger的日志類,完成日志類的封裝,編輯如下代碼:

'''
  Code description:封裝日志類,定義日志文件輸出格式和日志輸出級別
  Create time:2018-11-8
  Developer:
'''
# -*- coding: utf-8 -*-
import logging
import time
import os.path
class Logger(object):
    def __init__(self,logger,CmdLevel = logging.INFO,FileLevel = logging.INFO):
        self.logger = logging.getLogger(logger)
        self.logger.setLevel(logging.DEBUG)   # 設置日志默認級別為DEBUG
        fmt = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s') # 設置日志輸出格式
        currTime = time.strftime('%Y%m%d%H%M',time.localtime(time.time()))  # 格式化當前時間
        log_path = os.path.dirname(os.path.abspath('E:\V2200_AutoTest\\testcase')) + '/log/logs/'  # 設置日志文件保存路徑
        # log_path = os.path.dirname(os.path.abspath('.')) + '/log/logs/'      # 相對路徑寫法
        # print(os.path.dirname(os.path.abspath('E:\V2200_AutoTest\\testcase')))
        print('得到的日志路徑為:', log_path)
        log_name = log_path + currTime + '.log'  # 設置日志文件名稱

        # 設置由文件輸出
        fh = logging.FileHandler(log_name,encoding='utf-8')  # 采用utf-8字符集格式防止出現中文亂碼
        fh.setFormatter(fmt)
        fh.setLevel(FileLevel)  # 日志級別為INFO

        # 設置日志由控制台輸出
        # sh = logging.StreamHandler(log_name)
        # sh.setFormatter(fmt)
        # sh.setLevel(CmdLevel)
        self.logger.addHandler(fh)  # 添加handler
    def getlog(self):
        return self.logger

     這樣我們就自定義封裝了一個簡單的日志類模塊,設置了日志輸出級別、輸出格式以及輸出日志的位置,后面其他模塊需要輸出日志時,就可以調用引入該日志類。

 五、瀏覽器模塊的實現

     日志類實現簡單封裝后,繼續造輪子~。此部分用於封裝瀏覽器模塊,主要實現打開和關閉不同瀏覽器的方法,這里就用到了POM的思想,咱們封裝了瀏覽器的類型和打開關閉方法,那么后面每條測試腳本就可以直接調用打開和關閉瀏覽器方法,腳本只需要專注具體的測試業務邏輯的實現即可。在models目錄下新建broser_engine.py文件,自定義一個叫做BrowserEngine類,實現瀏覽器模塊的封裝,代碼如下:

'''
  Code description:封裝瀏覽器引擎類,讀取配置文件實現瀏覽器類型的選擇,並封裝打開瀏覽器和退出的方法
  Create time:2018-11-12
  Developer:
'''
# -*- coding: utf-8 -*-
import configparser          # python解析配置文件模塊
import os.path
from selenium import webdriver
from V2200.test.models.log import Logger     # 引入日志類模塊

logger = Logger(logger="BrowserEngine").getlog()    # 實例化對象logger

class BrowserEngine(object):
    def __init__(self, driver):
        self.driver = driver   # 初始化構造函數,將參數driver self化便於后面創建的方法直接自動調用
    def open_browser(self, driver):
        '''
        :param driver: 讀取配置文件,返回driver
        :return:
        '''
        config = configparser.ConfigParser()
        file_path = os.path.abspath('E:\V2200_AutoTest\V2200\config\config.ini')   # 絕對路徑寫法
        #print('得到的讀取config文件的路徑:',file_path)
        config.read(file_path,encoding='UTF-8')    # 讀取配置文件
        browser = config.get("browserType", "browserName")
        logger.info("選擇的瀏覽器是: %s ." % browser)
        url = config.get("testServer", "URL")
        logger.info("測試的平台URL是: %s" % url)

        if browser == "Firefox":
            driver = webdriver.Firefox()
            logger.info("Starting firefox browser.")
        elif browser == "Chrome":
            driver = webdriver.Chrome()
            logger.info("Starting Chrome browser.")
        elif browser == "Ie":
            driver = webdriver.Ie()
            logger.info("Starting IE browser.")

        driver.get(url)                        # 得到測試的url
        logger.info("瀏覽器的版本為:%s" % driver.capabilities['version'])  # 獲取瀏覽器版本
        driver.maximize_window()
        logger.info("最大化瀏覽器窗口.")
        driver.implicitly_wait(10)
        return driver

    def quit_browser(self):
        self.driver.quit()

 六、頁面基類的實現

        此部分用於封裝頁面基類,主要用於封裝一些常用的公共方法,如截圖方法、元素定位方法、元素通用操作方法、警示框處理方法等等,只要軟件頁面一些常用的操作都可以寫在該頁面基類中,這個頁面基礎類就類似於一個公共函數庫一樣,封裝這些方法,后面有需要的地方直接調用即可。如下代碼,已經封裝了8大元素定位方法、截圖方法、鼠標點擊方法、警示框處理方法等,后續根據自己的需要自行補充豐富一些常用的功能函數或者方法。這里可以着重看下8大元素定位方法的封裝~

 

'''
  Code description:頁面基類,封裝所有頁面共用的方法
  Create time:2018-11-13
  Developer:
'''
# -*- coding: utf-8 -*-
import time
import os.path
from V2200.test.models.log import Logger
from selenium.common.exceptions import NoSuchElementException    # selenium下封裝的判斷元素是否存在的模塊
logger = Logger(logger='BasePage').getlog()
class BasePage(object):
    # 構造方法,初始化參數driver,用於后面的方法直接調用
    def __init__(self,driver):
        self.driver = driver
    # 瀏覽器前進
    def forward_browser(self):
        self.driver.forward()
        logger.info("在當前頁面中點擊瀏覽器前進.")
    # 瀏覽器后退
    def back_browser(self):
        self.driver.back()
        logger.info("在當前頁面中點擊瀏覽器后退.")
    # 設置隱式等待時間
    def wait(self,seconds):
        self.driver.implicitly_wait(seconds)
        logger.info("設置隱式時間:%d 秒." % seconds)
    # 關閉當前窗口
    def close_window(self):
        try:
            self.driver.close()
            logger.info("關閉當前窗口.")
        except NameError as e:
            logger.error("關閉當前窗口出錯,拋出錯誤提示:%s." % e)
    # 截圖功能:得到截圖並保存圖片到項目image目錄下
    def get_window_img(self):
        file_path = os.path.dirname(os.path.abspath('.')) + '/image/'  # 設置存放截圖的路徑
        # print('截圖保存路徑為:%s' % file_path)
        timeset = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))       # 格式化時間
        pic_name = file_path + timeset + '.png'                                   # 定義截圖文件名稱
        try:
            self.driver.get_screenshot_as_file(pic_name)
            logger.info('截圖成功,圖片保存路徑為:/image.')
        except Exception as e :
            logger.error('截圖出現異常',format(e))
            self.get_window_img()

    # 8大頁面元素(對象)定位方法的封裝
    def find_element(self,selector):
        '''
        使用‘=>’作為字符串分割符,后續實際測試用例根據輸入的元素selector_by和selector_value 進行選擇元素的定位類型
        :param selector:
        :return: element
        '''
        element = ''
        if '=>' not in selector:
            return self.driver.find_element_by_id(selector)
        selector_by = selector.split('=>')[0]   # 按=>分割符進行切割字符串,返回一個列表,得到列表的第一個元素,即元素的定位方法
        selector_value = selector.split('=>')[1]  # 得到列表的第二個元素,即元素定位的值

        if selector_by == 'i' or selector_by == 'id':
            try:
                element = self.driver.find_element_by_id(selector_value)
                logger.info("定位元素OK,實際定位元素方法:%s ,定位的元素的屬性值:%s" % (selector_by,selector_value))
            except NoSuchElementException as e:
                logger.error("沒找到元素,拋出異常:%s" % e)
                self.get_window_img()  # 截取當前窗口
        elif selector_by == 'n' or selector_by == 'name':
            element = self.driver.find_element_by_name(selector_value)
        elif selector_by == 'c' or selector_by == 'class_name':
            element = self.driver.find_element_by_class_name(selector_value)
        elif selector_by == 'l' or selector_by == 'link_text':
            element = self.driver.find_element_by_link_text(selector_value)
        elif selector_by == 'p' or selector_by == 'partial_link_text':
            element = self.driver.find_element_by_partial_link_text(selector_value)
        elif selector_by == 't' or selector_by == 'tag_name':
            element = self.driver.find_element_by_tag_name(selector_value)
        elif selector_by == 'x' or selector_by == 'xpath':
            try:
                element = self.driver.find_element_by_xpath(selector_value)
                logger.info("定位元素OK,實際定位元素方法:%s ,定位的元素的屬性值:%s" % (selector_by, selector_value))
            except NoSuchElementException as e:
                logger.error("沒找到元素,拋出異常:%s" % e)
                self.get_window_img()  # 截取當前窗口
        elif selector_by == 'c' or selector_by == 'css_selector':
            element = self.driver.find_element_by_css_selector(selector_value)
        else:
            raise NameError("請輸入正確的目標元素類型.")
        return element  # 返回變量element

    # 封裝輸入框方法
    def type(self,selector,text):
        el = self.find_element(selector)
        el.clear()
        try:
            el.send_keys(text)
            logger.info("輸入的文本內容為:%s" % text)
        except NameError as e:
            logger.error("輸入的內容異常,拋出異常:%s" % e)
            self.get_window_img()

    # 清除文本內容
    def clear(self,selector):
        el = self.find_element(selector)
        try:
            el.clear()
            logger.info("清除輸入框文本信息OK")
        except NameError as e:
            logger.error("清除輸入框內容失敗:拋出異常: %s" % e)
            self.get_window_img()
    # 封裝點擊元素的動作
    def click(self,selector):
        el = self.find_element(selector)
        try:
            el.click()
            logger.info("點擊元素動作完成")
        except NameError as e:
            logger.error("點擊事件失敗,拋出異常:%s" % e)
    # 獲取打開的url地址標題
    def get_page_title(self):
        logger.info("當前打開的url地址標題為:%s" % self.driver.title)
        return self.driver.title

    # 獲取警示框,並得到提示框信息和關閉提示框
    def get_alert(self):
        el = self.driver.switch_to.alert    # 獲取窗口彈窗的方法
        try:
            assert '用戶名或者密碼錯誤' in el.text    # el.text方法獲取提示框內容
            logger.info("彈窗提示正確")
            el.accept()                              # 點擊彈窗確認按鈕
        except Exception as e:
            print('彈窗提示錯誤', format(e))

    @staticmethod        #  靜態方法:不強制要求傳遞參數,類可以不用實例化就能調用該方法
    def sleep(seconds):
        time.sleep(seconds)
        logger.info("等待時間是:%s 秒" % seconds)

 七、登陸頁面元素的封裝

        在上面我們實現了頁面基類的封裝,下圖為樓主公司的一個軟件登陸頁面,在page_obj目錄下新建home_page.py實現這個登陸頁面元素定位和元素操作方法的封裝

 

      代碼如下:

'''
  Code description: 繼承基類,封裝登陸頁面所有的元素和元素的操作方法
  Create time:2018-11-16
  Developer:
'''
# -*- coding: utf-8 -*-
from V2200.test.models.base_page import BasePage
import xlrd                                    # excel操作相關的模塊
from V2200.test.models.log import Logger
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陸頁面業務組件')
logger = Logger(logger='HomePage').getlog()
class HomePage(BasePage):
    def __init__(self,driver):
        BasePage.__init__(self,driver)        # 繼承父類,並調用父類的初始化方法
        self.input_username = table_sheetName.cell(1,1).value   # 讀取excel表中用戶名輸入框元素
        self.input_password = table_sheetName.cell(2,1).value   # 讀取excel表中密碼輸入元素
        self.rempwd = table_sheetName.cell(3,1).value           # 讀取excel表中是否記住密碼按鈕元素
        self.loginBtn = table_sheetName.cell(4,1).value         # 讀取excel表中登陸按鈕元素
        self.centerBtn = table_sheetName.cell(6,1).value        # 讀取excel表中切換到中心用戶的按鈕
        logger.info("讀取excel文件中登陸頁面相關元素數據完成")
    def center_user(self):
        self.click(self.centerBtn)
    def user(self,text):
        self.type(self.input_username,text)
    def pwd(self,text):
        self.type(self.input_password,text)
    def ifrempwd(self):
        self.click(self.rempwd)
    def login(self):
        self.click(self.loginBtn)

  這里引入了第三方的xlrd模塊,用於讀取excel文件中的數據,當然還有xlwt模塊用於向excel寫數據,說白了這兩個模塊就是實現操作excel,上面代碼只用到了xlrd模塊,在編寫上面登陸頁面的封裝前,咱們先將登陸頁面定位的元素和元素屬性寫到對應的excel表中,這樣做的好處就是實現測試數據和測試腳本的分離,如果頁面元素發生變化,那么我們就只需要修改excel中的元素屬性而不需要修改代碼,在data/testdata目錄下新建名稱為elementData.xlsx的文件,excel編輯內容如下:

    后續各個頁面的元素都是定位以及元素的數據都是可以寫在不同的sheet中,至於xlrd模塊具體向excel中讀數據的方法以及使用,這里也是不做介紹,自己百度學習練習下就知道了。

軟件其他頁面的封裝也是類似,按照上面的思想來就OK了。

 八、登陸頁面測試腳本的編寫

    通過上面封裝的基類和登陸頁面類,在unittest框架下開始編寫具體的測試腳本。測試腳本在testcase下,如登陸功能的腳本我們寫在:testcase/login_page_case目錄下,其他頁面的腳本寫在對應的目錄下。在testcase/login_page_case目錄下創建:test_login_success.py和test_login_unsuccess.py分別表示登陸成功的腳本和登陸不成功的腳本。如下代碼:

登陸成功代碼:

'''
  Code description:測試登陸  Create time:2018-11-20  Developer:
'''
# -*- coding: utf-8 -*-
import unittest
# unittest執行測試用例,默認是根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。
import time
from V2200.test.models.browser_engine import BrowserEngine
from V2200.test.page_obj.home_page import HomePage
import xlrd
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陸頁面業務組件')
class Login(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # 測試前置條件
        browser = BrowserEngine(cls)
        cls.driver = browser.open_browser(cls)
    @classmethod
    def tearDownClass(cls):
        # 測試結束后環境的復原
        cls.driver.quit()
# case1:正確的用戶密碼登陸
    def test_1_login_sucess(self):
        homepage = HomePage(self.driver)
        homepage.user(table_sheetName.cell(1,2).value)     # 讀取excel中的數據
        homepage.pwd(table_sheetName.cell(2,2).value)
        homepage.ifrempwd()
        homepage.login()
        time.sleep(2)
        try:
            assert '視頻監控' in homepage.get_page_title()
            print('test title success')
            homepage.get_window_img()  # 調用Basepage類封裝的截圖方法
        except Exception as e:
            print('test title error', format(e))

if  __name__  ==  '__main__':
    unittest.main()            # 將一個單元測試模塊變成可以直接運行的測試腳本

  登陸不成功代碼:

'''
  Code description:測試登陸  Create time:2018-11-20  Developer:
'''
# -*- coding: utf-8 -*-
import unittest
# unittest執行測試用例,默認是根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。
import time
from V2200.test.models.browser_engine import BrowserEngine
from V2200.test.page_obj.home_page import HomePage
import xlrd
# from V2200.test.page_obj.link_page import LinkPage
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陸頁面業務組件')
class LoginUnsuccess(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # 測試前置條件
        browser = BrowserEngine(cls)
        cls.driver = browser.open_browser(cls)
    @classmethod
    def tearDownClass(cls):
        # 測試結束后環境的復原
        cls.driver.quit()
# case2:錯誤的用戶+正確的密碼登陸
    def test_2_login_erroruser(self):
        homepage = HomePage(self.driver)
        homepage.user(table_sheetName.cell(1, 3).value)     # 讀取excel中的數據
        homepage.pwd(table_sheetName.cell(2, 3).value)
        homepage.ifrempwd()
        homepage.login()
        time.sleep(2)
        try:
            assert '視頻監控' in homepage.get_page_title()
            print('test title success')
            homepage.get_window_img()  # 調用Basepage類封裝的截圖方法
        except Exception as e:
            print('test title error',format(e))

# case3:正確的用戶+錯誤的密碼登陸
    def test_3_login_errorpasswd(self):
        homepage = HomePage(self.driver)
        homepage.user(table_sheetName.cell(1, 4).value)     # 讀取excel中的數據
        homepage.pwd(table_sheetName.cell(2, 4).value)
        homepage.ifrempwd()
        homepage.login()
        time.sleep(2)
        try:
            assert '視頻監控' in homepage.get_page_title()
            print('test title success')
            homepage.get_window_img()  # 調用Basepage類封裝的截圖方法
        except Exception as e:
            print('test title error', format(e))
# case4:錯誤的用戶+錯誤的密碼登陸
    def test_4_login_erroruser_errorpasswd(self):
        homepage = HomePage(self.driver)
        homepage.user(table_sheetName.cell(1, 5).value)     # 讀取excel中的數據
        homepage.pwd(table_sheetName.cell(2, 5).value)
        homepage.ifrempwd()
        homepage.login()
        time.sleep(2)
        try:
            assert '視頻監控' in homepage.get_page_title()
            print('test title success')
            homepage.get_window_img()  # 調用Basepage類封裝的截圖方法
        except Exception as e:
            print('test title error', format(e))

if  __name__  ==  '__main__':
    unittest.main()            # 將一個單元測試模塊變成可以直接運行的測試腳本

 九、測試執行控制模塊

      完成上面的測試腳本編寫后,對於自動化測試還需要有一個測試執行控制的部分,用來控制執行哪些用例集,生成HTML可視化的測試報告,並實現測試報告郵件發送。

在runtest目錄下新建run_all_case.py,編輯如下代碼:

'''
  Code description: TestLoader測試case,並執行得到的所有測試集,生成html文件的測試報告並郵件發送測試報告
  Create time:2018-11-20
  Developer:
'''
# -*- coding: utf-8 -*-
import HTMLTestRunner1     # 導入開源的測試報告生成HTML格式的模塊
import os.path
import time
import unittest
import configparser        # 解析配置文件模塊
from email.mime.text import MIMEText
from email.header import Header
import smtplib
"""
發郵件需要用到python兩個模塊,smtplib和email,這倆模塊是python自帶的,只需import即可使用。
smtplib模塊主要負責發送郵件,email模塊主要負責構造郵件。
其中MIMEText()定義郵件正文,Header()定義郵件標題。MIMEMulipart模塊構造帶附件

"""
# ===============定義郵件發送============
def send_mail(file_new):

    config = configparser.ConfigParser()
    file_path = os.path.dirname(os.path.abspath('.')) + '/V2200/config/config.ini'
    config.read(file_path, encoding='UTF-8')                 # 讀取config配置文件
    emailserver = config.get("emailserver", "emailservice")
    from_user = config.get("emailfrom_user", "from_user")
    from_passwd = config.get("emailfrom_passwd", "from_passwd")
    to_user = config.get("emailto", "to_user")

    f = open(file_new,'rb')
    mail_boy = f.read()
    f.close()
    msg = MIMEText(mail_boy,'html','utf-8')        # 定義郵件正文
    msg['Subject'] = Header('V2200自動化測試報告','utf-8')  # 定義郵件標題
    smtp = smtplib.SMTP()
    smtp.connect(emailserver)                     #  連接郵箱服務器
    smtp.login(from_user,from_passwd)   #  郵件發送方登陸
    smtp.sendmail(from_user,to_user,msg.as_string())  # 郵件發送者和接收者
    smtp.quit()
    print("郵件已經發送,請注意查收!")

# ==============找到最新生成的測試報告文件===========
def new_report(report_path):
    lists = os.listdir(report_path)   # 得到項目目錄下所有的文件和文件夾
    lists.sort(key=lambda fn:os.path.getmtime(report_path + '\\' + fn))  # 將得到的文件和文件夾按創建時間排序
    file_new = os.path.join(report_path,lists[-1])  # 獲取最新創建的文件
    print(file_new)
    return file_new
# 測試用例路徑
# case_path = os.path.join(os.getcwd(),'testcase')
case_path = os.path.abspath('E:\V2200_AutoTest\\testcase')
print(case_path)
# 測試報告路徑
report_path = os.path.abspath('E:\V2200_AutoTest\\testreport')
print(report_path)
def all_case():
    '''
    找到case_path路徑下所有以test_login開頭的測試用例文件,保證每個子目錄都是一個包文件,即該目錄下
    有__init__.py文件,才能獲取到多個目錄下的所有test*.py的文件下的所有測試用例
    '''
    all_case = unittest.defaultTestLoader.discover(case_path,pattern="test_login*.py",top_level_dir=None)
    print(all_case)
    return all_case
if __name__ == '__main__':
    # 獲取當前時間,並格式化時間
    now_time = time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))
    # html測試報告路徑
    report_html = os.path.join(report_path,"result_"+now_time+".html")
    fp = open(report_html,'wb')     # 打開一個文件,將測試結果寫入該文件中
    '''
    wb:以二進制格式打開一個文件只用於寫入。如果該文件已存在則打開文件,並從開頭開始編輯,
    即原有內容會被刪除。如果該文件不存在,創建新文件
    '''
    runner = HTMLTestRunner1.HTMLTestRunner(stream=fp,
                                           title=u'V2200自動化測試報告,測試結果如下:',
                                           description=u'用例執行情況:')

    runner.run(all_case())   # 執行所有測試case
    fp.close()
    mail_report = new_report(report_path)
    send_mail(mail_report)

  該代碼,編寫了怎么獲取需要執行的測試用例腳本和引入生成可視化測試報告的模塊和發送郵件模塊等,對於這幾個模塊自己多學習下就能掌握。

、測試執行效果

    通過上面這些類的封裝以及測試腳本的編寫,算是完成了我們自動化測試框架的基本具備的東西。忙活了這么久,是時候來看看咱們的效果了。PyCharm中運行run_all_case.py,運行完成后的效果如下:

 咱們再看看log/logs路徑下生成的日志,就如下圖這樣:

 同時執行完成后,在testreport目錄下會生成HTML格式的可視化測試報告文件,用瀏覽器打開效果如下:

 這報告是不是很酷炫啊,O(∩_∩)O哈哈~

     還有測試報告發送郵件給到指定的郵箱哦,如果你的自動化測試執行完了,可以把該自動化測試報告自動郵件發給你的leader,領導看到了是不是對你另眼相看?樓主上面的代碼設置發送的是樓主公司內網使用的郵箱:foxmail,效果如下:

到這里算是完成了咱們自動化測試框架,並取得了一定的成果~~

十一、整個自動化測試框架的總結和反思

        其實到第十節的介紹,樓主算是成功的做出了一個輕量級的測試框架,but,回過頭來繼續思考,還是有諸多需要優化和待下一步解決的問題:

1.頁面基類還需要補充更多的公共函數或者方法;

2.可視化HTML測試報告內容還不夠豐富,沒有完善的測試執行失敗的用例的詳細描述和測試截圖附件顯示;

3.整個框架的部分邏輯還需要優化和改進;

4.待解決的問題:沒實現測試腳本的持續集成和定時執行,現在想到的是配合jenkins持續集成來達到自動構建測試執行任務;

5.想獨立開發一個web測試平台,現在想到的是學習Django的web框架來開發一個自動化測試平台;

    對於這樣不足和構想,樓主也是會繼續學習相關的知識,並一步步實現它,對於看到該博客的朋友們也可以給樓主一些好的建議和指出錯誤,希望有對自動化測試有興趣的朋友,大家共同學習和進步哦。

 


免責聲明!

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



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