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

1、前言css

        前面系列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自動化測試框架是如何實現的吧~html

 二、項目架構說明python

       該項目架構基於樓主公司的一款B/S軟件設計,你們也能夠根據本身的被測軟件來構建適合本身的架構層級,固然也能夠參考樓主的。作自動化測試項目,當搞懂了思想和方法,其實都是萬變不離其宗,就跟寫代碼同樣,語言萬千種,惟一不變的就是語言中異曲同工的思想,所以,玩會了套路天然就能凌駕於套路之上,運用並加入本身的東西。在PyCharm中新建以下的項目層級:
web

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

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

 三、配置文件設計服務器

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

至於爲何這個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框架來開發一個自動化測試平臺;

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

相關文章
相關標籤/搜索