學精python selenium自動化只要讀完這一篇

大牛测试技术發表於2024-07-29

第一篇 基礎案例篇

大牛測試出品,影片/程式碼 專案案例請聯絡作者:2574674466
前言:
內卷時代測試人員如何面對? 逃避 還是提高自己?
為什麼要學習selenium?
內功如何修煉

學習目標:
學會selenium環境搭建、不同瀏覽器驅動
虛擬環境與程式碼異常原因分析
十六大元素定位
原始碼解讀-從原始碼中學習常用方法
特殊方法場景應用

1.1、Windows環境搭建

  • Python環境

官網:https://www.Python.org/

a、對應的作業系統對應的版本

b、安裝時選擇“Add Python 3.10 to PATH

  • Pycharm環境搭建

下載地址:https://www.jetbrains.com/pycharm/download/

  • pip介紹

  Python有成千上萬的寶藏庫,這些庫相當於是已經整合好的工具,只要安裝就能在Python裡使用。它們可以處理各式各樣的問題,無需你再造輪子,而且隨著社群的不斷更新維護,有些庫越來越強大,幾乎能媲美企業級應用。

怎麼下載安裝呢?它們被放在一個統一的”倉庫”裡,名叫PyPi(Python Package Index),所有的庫安裝都是從這裡排程。

有了倉庫之後,還需要有管理員,pip就是這樣一個角色。pip把庫從PyPi取出來,然後安裝到Python裡,還可以管理安裝好的庫,比如更新、檢視、搜尋、解除安裝等等

  換成國內源:

1、臨時方式

pip install -i http://mirrors.aliyun.com/pypi/simple/ --upgrade h5py --trusted-host mirrors.aliyun.com

  

2、永久方式

C:\Users\使用者名稱\AppData\Roaming 在Roaming資料夾下新建pip資料夾 pip.ini配置檔案,檔案內容如下:(快捷鍵:win +r ; 輸入%appdata%)

[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple [install] trusted-host=mirrors.aliyun.com

  

pip命令:

#pip install xx (selenium) 安裝軟體
#pip install selenium==3.6.0安裝版本是3.6.0 selenium
#pip install -U xx 更新 update縮寫
#pip uninstall Package 卸解軟體
#pip install xx –upgrade
#pip freeze 檢視已安裝版本或#pip list
#pip show xx 檢視某個包的版本
#python setup.py install   離線包安裝命令

pip官網:

https://pypi.org/project/selenium/#files

  • Selenium環境搭建

官網:https://www.selenium.dev/

  • 驅動安裝

1、谷歌驅動:http://npm.taobao.org/mirrors/chromedriver/

新版本驅動下載https://googlechromelabs.github.io/chrome-for-testing/

126版本:https://googlechromelabs.github.io/chrome-for-testing/

2、火狐驅動網址:

https://npm.taobao.org/mirrors/geckodriver/

3、Edge驅動網址:

https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

測試指令碼:

#coding=utf-8
from selenium import webdriver
path= '../chromedriver.exe'
#selenium4新增了Service方法
#path1=Service(r'D:\dr\geckodriver.exe')
driver = webdriver.Chrome(path)
driver.get('https://www.baidu.com/'

  

(1) pycharm自帶selenium模組,D:\soft\PyCharm 2021.3.2\plugins\python\helpers\typeshed\stubs\selenium

(2) 不同環境導致找不到庫

版本匹配異常

  • 1、下載mac版python

    2、預設安裝路徑:

    #/Library/Frameworks/Python.framework/Versions/3.11

    設定預設python版本:

    設定 Python 3 為預設版本,先開啟終端,輸入如下命令開啟配置檔案:
    #vim ~/.bash_profile
    在配置檔案中加入以下內容:
    alias python="/Library/Frameworks/Python.framework/Versions/3.11/bin/python3"
    alias pip="/Library/Frameworks/Python.framework/Versions/3.11/bin/pip3"
    儲存並退出,然後執行以下命令:
    #source ~/.bash_profile
    在命令列視窗中輸入“python”,顯示“Python 3.11.1”,說明版本切換成功
    

      

     

    3、驅動選擇mac專屬版

    #coding=utf-8
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    import time
    #driver路徑
    path =Service('/Users/tim/Downloads/chromedriver')
    driver = webdriver.Chrome(service=path)
    #開啟登入頁面
    driver.get('file:///Users/tim/Desktop/se/book/login.html')
    

      

    -----------------------------------------------------------------------------

    1.2、十六大定位

    元素定位意

    • ID

    例:素材為資料包中login.html網頁

    使用者名稱id為"dntest"

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    import time
    path =Service('/Users/tim/Downloads/chromedriver')
    driver = webdriver.Chrome(service=path)
    #開啟登入頁面
    driver.get('file:///Users/tim/Desktop/selenium/book/login.html')
    #使用者名稱輸入測試
    driver.find_element(By.ID,"dntest").send_keys("大牛測試")
    

      


    • NAME (素材為login.html網頁)

    例:使用者名稱文字框name為"daniu"

    def find_element(self, by=By.ID, value: Optional[str] = None) -> WebElement:
    """
     Find an element given a By strategy and locator.
    
     :Usage:
     ::
    
     element = driver.find_element(By.ID, 'foo')
    
     :rtype: WebElement
     """
    driver.find_element(By.NAME,"daniu").send_keys("大牛測試")
    

      

    • LINK_TEXT

    例:單擊login.html“上傳資料頁面”

    driver.find_element(By.LINK_TEXT,"上傳資料頁面").click()
    

      


    • PARTIAL_LINK_TEXT

    例:單擊login.html“上傳資料”

    driver.find_element(By.PARTIAL_LINK_TEXT,"上傳資料").click()
    

      


    • TAG_NAME

    例:login.html頁面獲取form標籤name屬性"daniu"並輸出

    print(driver.find_element(By.TAG_NAME,"form").get_attribute("name"))
    

      


    • CLASS_NAME

    例:login.html頁面密碼框:"passwd"

    driver.find_element(By.CLASS_NAME,"passwd").send_keys("testdaniu")
    

      

    • CSS_SELECTOR

    https://www.w3school.com.cn/cssref/css_selectors.asp

    例:class方式

    #text與phone之間空格加入“.”
    driver.find_element(By.CSS_SELECTOR,".f-text.phone-input").send_keys("技術")
    #用.f-text也可
    driver.find_element(By.CSS_SELECTOR,".f-text").send_keys("技術")
    #用class=’f-text phone-input’text與phone之間空格不需處理
    driver.find_element(By.CSS_SELECTOR,"[class='f-text phone-input']").send_keys("技術")
    

      

    id 方式

    driver.find_element(By.CSS_SELECTOR,"#dntest").send_keys("大牛測試")
    

      

    name方式

    driver.find_element(By.CSS_SELECTOR,"input[name='daniu']").send_keys("大牛測試")
    

      

    • XPATH

    絕對路徑:例,大牛測試使用者名稱 -- html/body/form/input[1]

    driver.find_element(By.XPATH,"html/body/form/input[1]").send_keys("大牛測試")
    

      

    相對路徑:

    #login.html登入使用者名稱標籤是“input”

    driver.find_element(By.XPATH,"//input[@id='dntest']").send_keys("技術")
    

      

    #多個屬性條件之間可用and

    (By.XPATH,"//*[@id='dntest'and @name='daniu']")
    

      

    #or表示只需一個屬性條件滿足即可

    (By.XPATH,"//*[@id='dntest' or @name='daniu']")
    

      

    模糊定位:

    starts-with與contains

    例:

    #login.html頁面,使用者名稱為例,id屬性"dntes"開頭定位
    By.XPATH,'//input[starts-with(@id,"dntes")]
    #login.html頁面,使用者名稱為例,id屬性"ntes"開頭定位
    By.XPATH,'//input[contains(@id,"ntes")]
    

      

    1.22 表格定位

    學習表格定位前,先熟悉python基礎,有基礎的同學飄過

    • python 變數不需要宣告型別
    x = 5 int型別
    x = "selenium"字串
    

      

    • 字串型別
    #coding=utf-8
    from_station = "杭州很漂亮"
    print(from_station)
    print(from_station[1:2])
    print(from_station[0:1])
    

      

    
    
    • if語句
    書寫規則
    #coding=utf-8
    a = 5
    if a > 5:
        print("數字大於5")
    elif a < 10:
        print("數字小於5")
    else:
        print("數字等於5")
    

      

     

    • 列表
    list1 = ["selenium","appium","python","automation"]
    

      

    對列表操作 取值:list1[1]、list1[2:4]、list1[-2] 增加:list1.append('') 實現在列表最後新增一個元素,並且一次只能新增一個元素 刪除: del list1[1] insert()方法:在特定位置上新增元素 list1.insert(0,"dntest") remove()方法:其作用是可以刪除列表中的特定元素 pop() 將列表中的最後一個元素返回,並將其從列表中刪除 def pop(self, *args, **kwargs): # real signature unknown """ Remove and return item at index (default last). Raises IndexError if list is empty or index is out of range. """ pass

    面試題append與extend區別:

    append:

    一次只能追加一個資料

    追加的資料是元組、列表、集合、字典,該函式會將他們當作一個整體追加到原來的列表中

    extend:

    該函式會將可迭代物件中的元素逐一新增到原列表中,

    單獨的整型、浮點型是不可以用extend函式的,如extend(3),extend(3.3)

    • for迴圈


    法一

    #coding=utf-8
    
     #使用for迴圈來遍歷列表list1中的所有的元素
     #第一種方式
     for l in list1:
       print(l)
    

      


    法二 range方式

    for index in range(len(list1)):
       print(list1[index])
    

      


    range

    range(start, stop[, step])


    start: 計數是從start開始的,預設是從0開始的。
    stop: 計數到stop結束,但是不包括stop自身。
    step: 步長,預設為1。
    例1:

    #coding=utf-8
    for i in range(10):
        print(i)
    print("***************")
    

      


    例2:

    for j in range(2,10,2):
        print(j)
    print("***
    

      

    • 元組:tuple


    用小括號表示

    tu = ('a', 'b', 6, 5)
    tu[0]
    tu[1:2]
    len(tu)
    #佔用控制元件
    #list1.__sizeof__()


    佔用空間小

    逐個分配空間

    • Python格式化字串


    ①、%s ,%d,%f(浮點型)

    str1="tim"
    price =3000
    print("【%s】,【%d】" %(str1,price))
    

      


    ②、format

    
    #下標占位
    print("【{1}】,【{0}】".format("daniutest",3000))
    #變數名
    print("【{name}】,【{price}】".format(name="daniutest",price=3000))


    ③、格式化f

    # 格式化fstr1 = 'timtest'
    price = 3000
    print(f'機構是:{str1},價格是:{price}')

    開啟資料包中table1.html網頁,學習表格定位

    例1:列印所有元素

    定位表格中具體元素舉例

    例:在被測試網頁的表格中定位第 3 行第 3 列的單元格,XPath 定位表示式為:

    //*[@id="table1"]/tbody/tr[3]/td[3] 對應的 Python 定位語句是:

    driver.find_element(By.XPATH, ‘//*[@id="table1"]/tbody/tr[3]/td[3]’) 

    在表示式中,tr[3]表示第 3 行,td[3]表示第 3 列。使用 CSS 定位,則表示式為:

    #table1 > tbody > tr:nth-child(3) > td:nth-child(3)

    ("By.CSS_SELECTOR","#table1 > tbody > tr:nth-child(3) > td:nth-child(3)") 

    • 關聯元素定位策略

    資料包中relative_locator1.html頁面

    例1:above

    使用關聯元素定位策略時,需要引入 locate_with,引入語句為“from selenium. webdriver.support.relative_locator import locate_with”。

    透過 Above 模式獲取 relative locator 物件。

    relative locator 物件以引數的形式傳入方法 find_element。

    #透過relative locator的above方式先獲取到password input,然後再獲取username input.
    username_locator = locate_with(By.TAG_NAME,"input").above({By.ID: "id_pass"})
    username = driver.find_element(username_locator)
    username.send_keys('大牛測試')
    

      

     

    例2:below

    Below 模式使用方式為“locate_with(By.TAG_NAME,"input").below”。

    password_locator = locate_with(By.TAG_NAME,"input").below({By.ID: "id_username"})
    password = driver.find_element(password_locator)
    password.send_keys('daniu')
    

      

    例3:Left of

    案例:獲取登入按鈕,再執行取消

    cancel_locator = locate_with(By.TAG_NAME,"button").to_left_of({By.ID: "id_login"})
    cancel_element = driver.find_element(cancel_locator)
    print(cancel_element)
    

      

    例4:Right of

    #透過關聯元素定位策略的Left of模式先獲取“登入”按鈕,然後獲取“取消”按鈕 
    cancel_locator = locate_with(By.TAG_NAME,"button").to_left_of({By.ID: "id_login"})
    cancel_element = driver.find_element(cancel_locator)

    例5:Near模式,即目標元素在某元素的附近

    locator = locate_with(By.TAG_NAME,”label”).near({By.ID:“id_username”})
    element = driver.find_element(label_username_locator)
    print(label_username_element)

    例6: Chaining relative locators 模式

    目標元素的位置既滿足在元素 A 的 Above 位置,又滿足在元素 B 的 Right of 位置

    locator = locate_with(By.TAG_NAME,"button").below({By.ID:
    "id_label2"}).to_left_of({By.ID: "id_login"})
    element = driver.find_element(cancel_button_locator) #輸出元素物件
    print(cancel_button_element)

    ----------------------------------------------------------------------------

    1.3、原始碼中學習常用方法

    1. send_keys

    -- 應用場景:文字框輸值

    例:login.tml頁面輸入使用者名稱

    原始碼:

    def send_keys(self, *value) -> None:
        """Simulates typing into the element.
    
            :Args:
                - value - A string for typing, or setting form fields.  For setting
                  file inputs, this could be a local file path.
    
            Use this to send simple key events or to fill out form fields::
    
                form_textfield = driver.find_element(By.NAME, 'username')
                form_textfield.send_keys("admin")
    
            This can also be used to set file inputs.
    
            ::
    
                file_input = driver.find_element(By.NAME, 'profilePic')
                file_input.send_keys("path/to/profilepic.gif")
                # Generally it's better to wrap the file path in one of the methods
                # in os.path to return the actual path to support cross OS testing.
                # file_input.send_keys(os.path.abspath("path/to/profilepic.gif"))
            """
        # transfer file to another machine only if remote driver is used
        # the same behaviour as for java binding
        if self.parent._is_remote:
            local_files = list(
                map(
                    lambda keys_to_send: self.parent.file_detector.is_local_file(str(keys_to_send)),
                    "".join(map(str, value)).split("\n"),
                )
            )
            if None not in local_files:
                remote_files = []
                for file in local_files:
                    remote_files.append(self._upload(file))
                value = "\n".join(remote_files)
    
        self._execute(
            Command.SEND_KEYS_TO_ELEMENT, {"text": "".join(keys_to_typing(value)), "value": keys_to_typing(value)}
        )

    1. click

    --應用場景:單擊

    click 方法用於實現單擊操作,如 button 登入操作,以 login.html 頁面登入為例,示例 程式碼如下:

    driver.find_element(By.ID,"loginbtn").click() 

    1. get_attribute

    login.html頁面 class


    1. is_selected

    -- 應用場景:常用於檢查選擇框狀態是否選中

    返回值:True/False

    login.html 頁面 男/女

    print(driver.find_element(By.NAME,"checkbox1").is_selected()) 
    print(driver.find_element(By.NAME,"checkbox2").is_selected()) 

    1. is_enabled

    返回值:True/False

    判斷頁面元素是否可用,可用則返回 True,不可用則返回 False。 如 login.html 使用者名稱為可用狀態,則返回 True,示例程式碼如下

    print(driver.find_element(By.ID,"dntest").is_enabled) 
     def is_enabled(self) -> bool:
            """Returns whether the element is enabled."""
            return self._execute(Command.IS_ELEMENT_ENABLED)["value"]


    1. is_displayed

    -- 應用場景:是否顯示,肉眼可見

    返回值:True/False

    print(driver.find_element(By.ID,"dntest").is_displayed) 

    1. maximize_window

    -- 最大化

    driver.maximize_window()

    1. fullscreen_window

    -- 全屏

    driver.fullscreen_window() 
    


    1. minimize_window

    -- 最小化

    driver.minimize_window() 

    1. text

    -- linkText("上傳附件")

    driver.find_element(By.LINK_TEXT,"上傳資料頁面").get_attribute 
    ('textContent') 
    driver.find_element(By.LINK_TEXT,"上傳資料頁面").text

    1. clear

    應用場景:清空,如有初始值先“清空”

    login.html使用者名稱

    driver.find_element(By.ID,"dntest").clear() 

    原始碼:

        def clear(self) -> None:
            """Clears the text if it's a text entry element."""
            self._execute(Command.CLEAR_ELEMENT)

    1. driver.back() 後退

    #瀏覽器工具欄向後操作
    driver.back();

    1. driver.forward() 前進

    #瀏覽器工具欄向前操作
    driver.forward();

    1. close()

    關閉瀏覽器,多個tab只關閉當前。老版本driver不會退出


    1. quit

    關閉並driver退出,多個tab一起關閉

    1. current_url

    login.html頁面

    print(driver.current_url) 

    1. driver.current_window_handle

    current_window_handle 方法用於返回視窗控制代碼,即標識視窗字串

    driver.current_window_handle

    1. refresh

    -- 重新整理

    #重新整理當前頁面
    driver.refresh() 

    1. title

    -- 如:login.html,title為"大牛測試"

    #在控制檯上列印 title“大牛測試”
    print(driver.title) 
    1. page_source

    -- page_source 方法用於輸出網頁原始碼

    print(driver.page_source) 

    1. get_screenshot_as_base64()

    圖片編碼線上轉換

    https://tool.jisuapi.com/base642pic.html


    1. name

    返回當前執行的瀏覽器名稱, 如:輸出firefox

    print(driver.name) 

    23.set_window_size(800,600)

    set_window_size 方法用於設定瀏覽器當前視窗大小,設定視窗寬度為 800、高度為 600,示例程式碼如下:

    driver.set_window_size(800,600) 
    

    24.driver.get_window_size()

    get_window_size 方法用於獲取瀏覽器當前視窗的高度與寬度,示例程式碼如下

    driver.get_window_size()

    25. set_window_position

    set_window_position 方法用於設定瀏覽器當前視窗的位置,設定 x,y 為 0,0,示例程式碼 如下:

    driver.set_window_position(0,0) 


    26. window_handles

    window_handles 方法用於獲取多視窗控制代碼,返回列表型別,如開啟 login.html 頁面, 單擊“上傳資料頁面”後,輸出所有視窗控制代碼,程式碼如下

    #列印當前視窗控制代碼
    driver.find_element(By.PARTIAL_LINK_TEXT,"上傳資料").click() 
    print(driver.window_handles) 

    1.4、特殊方法

    1、下拉框,以select.html為例
    ●直接法

    如選擇第3個選項,定位語句為:

    "/html/body/form/select/option[3]"


    ●Select
    列印所有選項
    all_selected_options,返回下拉框中已經選中的選項,程式碼如下:
    first_selected_option,返回第一個被選中的選項:

    例1:

    # select_element = driver.find_element(By.ID,"select_id")
    # #index索引是從0開始的,如下程式碼選擇1,則是表示第二個選項。
    # Select(select_element).select_by_index(1)
    

    例2:

    se = driver.find_element(By.NAME,"selectList")
    # Select(se).select_by_visible_text("每頁顯示20條")
    ops = Select(se).first_selected_option
    print(ops.text)



    2、滑鼠事件
    滑鼠懸停即當游標與其名稱表示的元素重疊時觸發的事件,Selenium中對鍵盤滑鼠操作封裝在Action Chains類中。Action Chains類的主要應用場景為單擊滑鼠、雙擊滑鼠、滑鼠拖拽等。部分常用的方法使用分類如下:
    滑鼠事件:
    click(on_element=None),模擬滑鼠單擊操作。
    • click_and_hold(on_element=None),模擬滑鼠單擊並且按住不放。
    • double_click(on_element=None),模擬滑鼠雙擊。
    • context_click(on_element=None),模擬滑鼠右擊操作。
    • drag_and_drop(source,target),模擬滑鼠拖拽。
    • drag_and_drop(source,xoffset,yoffset),模擬將目標拖拽到目標位置。
    • key_down(value,element=None),模擬按住某個鍵,實現快捷鍵操作。
    • key_up(value,element=None),模擬鬆開某個鍵,一般和key_down操作一起使用
    • move_to_element(to_element),模擬將滑鼠移到指定的某個頁面元素
    • move_to_element_with_offset(to_element,xoffset,yoffset),移動滑鼠至指定的座標
    • perform(),將之前一系列的ActionChains執行。
    • release(on_element=None),釋放按下的滑鼠
    例1、move_to_element
    #from selenium.webdriver.common.action_chains import ActionChains

    driver.get('file:///Users/tim/Desktop/selenium/book/hover.html')
    #滑鼠懸停在系統控制元件
    ActionChains(driver).move_to_element(driver.find_element(By.ID,"testdn")).perform()
    #單擊登入
    driver.find_element(By.ID,"dntest").click()


    例2、double_click 雙擊

    driver.get('file:///D:/selenium/book/login.html')
    element = driver.find_element(By.LINK_TEXT,"上傳資料頁面")
    #雙擊“上次資料頁面”
    ActionChains(driver).double_click(element).perform()

    例3、右擊context_click

    driver.get('file:///D:/selenium/book/login.html')
    element = driver.find_element(By.ID,"dntest")
    ActionChains(driver).context_click(element).perform()


    例4、對滑塊操作






    driver.get('https://passport.ctrip.com/user/reg/home')
    import time
    time.sleep(4)
    driver.find_element(By.XPATH,"//*[@id='agr_pop']/div[3]/a[2]").click()
    source = driver.find_element(By.CSS_SELECTOR,"#slideCode > div.cpt-drop-box > div.cpt-drop-btn")
    target= driver.find_element(By.CSS_SELECTOR,"#slideCode > div.cpt-drop-box > div.cpt-bg-bar")
    ActionChains(driver).drag_and_drop_by_offset(source,target.location.get("x"),target.location.get("Y")).perform()

    3、鍵盤事件

    • Keys.BACK_SPACE:刪除鍵。
    • Keys.SPACE:空格鍵。
    • Keys.TAB:Tab 鍵。
    • Keys.ESCAPE:回退鍵。
    • Keys.ENTER:Enter鍵。
    • Keys.CONTROL,"a":快捷鍵 Ctrl + A。
    • Keys.CONTROL,"x":快捷鍵 Ctrl + X。
    • Keys.CONTROL,"v":快捷鍵 Ctrl + V。
    • Keys.CONTROL,"c":快捷鍵 Ctrl + C。
    • Keys.F1:F1 鍵。
    • Keys.F12:F12 鍵。

    實現登入框輸入文字為“測試”並刪除輸入的最後一個字元,程式碼如下:

    driver.find_element(By.ID,"").send_keys("dntest")
    driver.find_element(By.ID,"").send_keys(Keys.BACK_SPACE)



    4、定位一組元素
    checkbox.html頁面為例,選擇所有測試類目

    elements = driver.find_elements(By.NAME,"daniu")
    #列印所有NAME為“daniu”元素,存放到列表中
    print(elements)
    #單擊第二個元素即Web自動化
    elements[1].click()
    



    5、frame操作
    frame標籤有frameset、frame、iframe三種,frameset跟其他普通標籤沒有區別,不會影響到正常的定位,frame/iframe一樣
    id、name、物件
    ● switch_to.frame
    ● switch_to.default_content()
    ● switch_to.parent_frame()多層巢狀frame,切回

    原始碼:

        def frame(self, frame_reference: Union[str, int, WebElement]) -> None:
            """Switches focus to the specified frame, by index, name, or
            webelement.
    
            :Args:
             - frame_reference: The name of the window to switch to, an integer representing the index,
                                or a webelement that is an (i)frame to switch to.
    
            :Usage:
                ::
    
                    driver.switch_to.frame('frame_name')
                    driver.switch_to.frame(1)
                    driver.switch_to.frame(driver.find_elements(By.TAG_NAME, "iframe")[0])
            """
            if isinstance(frame_reference, str):
                try:
                    frame_reference = self._driver.find_element(By.ID, frame_reference)
                except NoSuchElementException:
                    try:
                        frame_reference = self._driver.find_element(By.NAME, frame_reference)
                    except NoSuchElementException as exc:
                        raise NoSuchFrameException(frame_reference) from exc
    
            self._driver.execute(Command.SWITCH_TO_FRAME, {"id": frame_reference})

    例1:

    driver.get('file:///D:/selenium/book/frame.html')
    # driver.switch_to.frame("frame")



    6、附件上傳、下載
    上傳

    ●方法一、send_keys

    driver.find_element(By.ID,"upload").send_keys("D:/response.txt")


    ●方法二、autoit

    下載autoit工具,程式碼如下:

    ControlFocus("開啟","","Edit") 
    WinWait("[CLASS:#32770]","",5) 
    #上傳 ip.txt 檔案
    ControlSetText("開啟","","Edit1","D:\soft\ip.txt") 
    Sleep(1000) 
    ControlClick("開啟","","Button1"); 


    ●方法三、pywinauto

    #pip install pywinauto

    #coding=utf-8 
    #引入 Application 模組
    from pywinauto.application import Application 
    import time 
    app =Application() 
    #定位到視窗
    app = app.connect(title_re="開啟",class_name="#32770") 
    #設定檔案路徑
    app['開啟']["EDit1"].SetEditText("D:\soft\ip.txt ") 
    time.sleep(2) 
    #單擊按鈕
    app["開啟"]["Button1"].click() 
    print("end") 

    下載

    driver.get('http://localhost:63342/pyse2023/chapter05/download.html?')
    driver.find_element(By.LINK_TEXT,'點選下載').click()


    7、多視窗切換
    功能:

    driver.get('file:///Users/tim/Desktop/selenium/book/login.html#')
    #列印當前視窗控制代碼
    print(driver.current_window_handle)
    driver.find_element(By.PARTIAL_LINK_TEXT,"上傳資料").click()
    #列印所有視窗控制代碼
    print(driver.window_handles)
    #切換都新頁面
    driver.switch_to.window(driver.window_handles[-1])
    #開啟上傳附件視窗
    driver.find_element(By.ID,"AttachFrame").click()

    def window(self, window_name) -> None:
    """
    Switches focus to the specified window.

        :Args:
         - window_name: The name or window handle of the window to switch to.



    8、彈框
    ●alert:用來提示

    參見al.html頁面
    ●confirm:用來確認
    ●prompt:輸入內容
    accept 接受
    dismiss()取消

    例:

    driver.get("file:///D:/selenium/book/al.html")
    #開啟alert彈框

    driver.find_element(By.ID,"alert").click()
    #切換到 alert

    al = driver.switch_to.alert
    #執行接受即消除彈
    al.accept()

    9、顏色驗證

    from selenium.webdriver.support.colorimportColor
    driver.get("http://localhost/login")
    #獲取的背景色 #1c84c6
    # baidu_background_color = Color.from_string(driver.find_element(By.CLASS_NAME,'imgcode').value_of_css_property('background-color'))
    #btnSubmit
    baidu_background_color=Color.from_string(driver.find_element(By.ID,'btnSubmit').value_of_css_property('background-color'))
    print(baidu_background_color.hex)



    編寫郵件(包含主旨、附件、正文)自動傳送到郵箱2574674466@qq.com

    --------------------------------------------------------------------------------------

    第二章 基礎加強

    學習目標:

    • ChromOptions細解
    • 從原始碼看等待時間
    • js與jquery高階應用
    • cookie登入
    • 驅動管理
    • 元素截圖以及By.ID列印後啟發

    2.1 、ChromOptions

    • "start-fullscreen";全屏
    • "headless";#無介面執行
    • "window-size=400,600";
    • "kiosk";無位址列
    • options.add_argument('--disable-infobars') # 禁止策略化

    add_argument('--incognito') # 隱身模式(無痕模式)

    add_argument('--disable-javascript') # 禁用javascript

    add_argument('--start-maximized') # 最大化執行(全屏視窗),

    add_argument('--hide-scrollbars') # 隱藏捲軸, 應對一些特殊頁面

    add_argument('blink-settings=imagesEnabled=false') # 不載入圖片, 提升速度

    例子:瀏覽器後臺執行

    options = webdriver.ChromeOptions()
    options.add_argument(xx)
    options = webdriver.ChromeOptions()
    #全屏
    options.add_argument("start-fullscreen")
    driver = webdriver.Chrome(service=path,options=options)
    driver.get("file:///D:/selenium/book/login.html")
    

    2.2、等待時間

    1. 強制等待 time.sleep()
    2. 隱性等待driver.implicitly_wait()

    設定的隱式等待時間為10秒

    1. 式(expected_conditions)

    如:WebDriverWait(driver,10,1)表示最長等待時間為 10s,每隔 1s 檢查 1 次元素,直至元素被定位

    By default, it is 0.5 second.

    POLL_FREQUENCY: float = 0.5

    class WebDriverWait:
        def __init__(
            self,
            driver,
            timeout: float,
            poll_frequency: float = POLL_FREQUENCY,
            ignored_exceptions: typing.Optional[WaitExcTypes] = None,
        ):
            """Constructor, takes a WebDriver instance and timeout in seconds.
    
            :Args:
             - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
             - timeout - Number of seconds before timing out
             - poll_frequency - sleep interval between calls
               By default, it is 0.5 second.
             - ignored_exceptions - iterable structure of exception classes ignored during calls.
               By default, it contains NoSuchElementException only.

    from  selenium.webdriver.support.ui import WebDriverWait
    from  selenium.webdriver.support import expected_conditions as ex
    locate = (By.ID,"dntest")
    WebDriverWait(driver,10,1).until(ex.visibility_of_element_located(locate))
    

    WebDriverWait

    • title_is

    判斷當前頁面的title是否完全等於XX

    • presence_of_element_located

    判斷某個locator元素是否被加到DOM樹裡,並不代表該元素一定可見(元素是隱藏的)

    • element_selection_state_to_be

    An Expectation for checking an element is visible and enabled such that
    you can click it.

    • visibility_of

    檢查元素存在DOM頁的,要能夠看得到

    • visibility_of_element_located

    判斷某個locator元素是否可見

    • element_to_be_clickable

    判斷某個locator元素是否可點選

    • element_to_be_selected

    等待element元素是被選中,一般用在下拉選單

    • element_located_to_be_selected

    等待locator元素是被選中

    """An expectation for the element to be located is selected.
    locator is a tuple of (by, path)"""

    2.3、JAVAScript與Jquery

    • js透過id操作

    document.getElementById('’).value='daniutest'

    driver.execute_script(js)

    • 捲軸window.scrollTo(0,1000) 縱向
    • 捲軸window.scrollTo(1000,0) 橫向

    使用 JavaScript 程式碼來執行對瀏覽器捲軸的操作,以 scroll.html 頁面為例,程式碼 如下:

    #coding=utf-8 
    from selenium import webdriver 
    from selenium.webdriver.chrome.service import Service 
    #driver 地址
    path =Service('D:\\dr\\chromedriver') 
    driver = webdriver.Chrome(service=path) 
    driver.maximize_window() 
    driver.get("file:///D:/selenium/book/scroll.html") 
    #設定瀏覽器視窗大小,目的是讓捲軸顯示出來
    driver.set_window_size(600,400) 
    js = "window.scrollTo(100,300)" 
    driver.execute_script(js) 

    jquery 是一個快速、簡潔 JavaScript 庫

    網址:https://www.w3school.com.cn/jquery/jquery_ref_selectors.asp

    例1:

     #coding=utf-8
     from selenium import webdriver
     driver = webdriver.Chrome()
     driver.maximize_window()
     driver.get("xx")
     jq = "$('#id').val('dntest')"
     driver.execute_script(jq)

    例2:#以下程式碼實現單擊登入按鈕

     jq = "$('').click()"
     driver.execute_script(jq)
     driver.quit()
    • 複數 document.getElementsByName("wd")

    • 自定義屬性:
    element.setAttribute("dn","www");
    element.getAttribute

    • 同步執行與非同步執行

    execute_script 是同步執行javascript程式碼。

    execute_async_script 非同步執行程式碼,WebDriver不會等待非同步執行的程式碼的結果,直接執行以下的程式碼。

    1.4、Cookie操作

    • 字典

    字典也是 Python 提供的一種常用的資料結構,它用於存放具有對映關係的資料

    dic ={"a":"selenium","b":"appium","c":"ui"}
    print(dic.get("b"))
    for k in dic:
        print (k)
        print(dic[k])

    淺複製:

    深考貝:

    • 淺複製:意思是就 複製了,但又複製得不徹底。淺複製之後,新物件確實是生成了,但在某些情況下新物件還和被複製物件有千絲萬縷的聯絡。
    • 深複製:深複製是真正的複製,一股腦兒都複製一個新,是徹底的複製,複製以後與被複製物件不再有任何瓜葛

    面試題:請講述 python字典 淺複製與深複製:

    #深複製會完全複製父物件及父物件內部的子物件,而淺複製會複製父物件,但不會複製父物件內部的子物件

    from copy import deepcopy
    
    dic = {"a":[1,1,1],"b":[2,2,2],"c":[4,4,4],"d":[5,5,5]}
    
    d1 = deepcopy(dic) #深複製
    print("d1=",d1)
    d2 = dic.copy() #淺複製
    print("d2=",d2)
    print("--------深複製修改子物件--------")
    #對字典內部子物件列表修改
    d1["c"].append()
    d1["d"].append(111)
    print("子物件修改後d1:\n ",d1)
    print("深複製的父物件:\n ",dic) #父物件沒有修改
    print("--------淺複製修改子物件--------")
    d2["c"].append(222)
    d2["d"].append(222)
    print("子物件修改後d2:\n ",d2)
    print("淺複製的父物件:\n ",dic) #父物件被修改了

    • Cookie常用方法:

    Cookie操作方法

    方法描述

    add_cookie(cookie_dict)

    在當前會話中新增cookie資訊,並且引數是屬於字典型別資料

    delete_all_cookies()

    刪除所有cookie資訊

    delete_cookie(cookie_name)

    刪除單個名字為“cookie_name”的cookie資訊

    get_cookie(cookie_name)

    返回名為“cookie_name”的cookie資訊

    get_cookies()

    返回當前會話所有的cookie資訊

    百度網網盤登入前後cookie比較:

    2.5 innerText 與 innerHTML

    innerText 屬性將文字內容設定或返回為指定節點及其所有子節點的純文字,如 inner.html 頁面對標籤

    操作,輸出字元“大牛測試”

    InnerHTML 允許使用 HTML 格式的文字,並且不會自動對文字進行編碼和解碼,如 inner.html 頁面對標籤<p>操作,輸出“<b>大牛測試</b>”,

    driver = webdriver.Chrome(service=path) 
    driver.maximize_window() 
    driver.get("file:///D:/selenium/book/inner.html") 
    #輸出 innerText 值“大牛測試”
    print(driver.find_element(By.ID,"dntest").get_attribute("innerText")) 
    #輸出 innerHTML 值“<b>大牛測試</b>”
    #print(driver.find_element(By.ID,"dntest").get_attribute("innerHTML"))

    2.6元素截圖

    方法一、直接截圖

    
    driver.get("file:///Users/tim/Desktop/selenium/book/login.html") 
    driver.save_screenshot("verification_code.png") 
    imglogin=driver.find_element(By.ID,"img") 
    imglogin.screenshot("login.png")

    方法二、先截整張、再截部分

    安裝pillow庫 # pip install pillow

    from PIL import Image 
    driver = webdriver.Chrome() 
    driver.get("file:///Users/tim/Desktop/selenium/book/login.html") 
    time.sleep(2) 
    driver.save_screenshot("code.png") 
    imgcode=driver.find_element(By.ID,"loginbtn") 
    #登入位置 left 值
    left= imgcode.location['x'] 
    #登入位置 top 值
    top= imgcode.location['y'] 
    #加上寬度
    right = left+imgcode.size['width'] 
    #加上高度
    bottom = top+imgcode.size['height'] 
    im = Image.open("vcode.png") 
    im = im.crop((left,top,right,bottom)) 
    im.save('login.png') 
    

    2.7 驅動管理

    Selenium 4 支援驅動管理模式,使用 WebDriver Manager 模式進行驅動管理時首先需 要下載 WebDriver Manager 模組,命令為“pip install webdriver-manager”

    from selenium import webdriver 
    from selenium.webdriver.chrome.service import Service 
    from webdriver_manager.chrome import ChromeDriverManager 
    driver = webdriver.Chrome(service=Service 
    (ChromeDriverManager().install())) 
    driver.get("file:///Users/tim/Desktop/selenium/book/login.html"

    原始碼:

    class ChromeDriverManager(DriverManager):
        def __init__(
                self,
                driver_version: Optional[str] = None,
                name: str = "chromedriver",
                url: str = "https://chromedriver.storage.googleapis.com",
                latest_release_url: str = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE",
                chrome_type: str = ChromeType.GOOGLE,
                download_manager: Optional[DownloadManager] = None,
                cache_manager: Optional[DriverCacheManager] = None,
                os_system_manager: Optional[OperationSystemManager] = None
        ):
            super().__init__(
                download_manager=download_manager,
                cache_manager=cache_manager,
                os_system_manager=os_system_manager
            )
    
            self.driver = ChromeDriver(
                name=name,
                driver_version=driver_version,
                url=url,
                latest_release_url=latest_release_url,
                chrome_type=chrome_type,
                http_client=self.http_client,
                os_system_manager=os_system_manager
            )
    
        def install(self) -> str:
            driver_path = self._get_driver_binary_path(self.driver)
            print(driver_path)
            os.chmod(driver_path, 0o755)
            return driver_path

    面試題:你是如何管理驅動的?

    2.8 透過原始碼理解By.ID

    例: 輸出 By.ID 值,思考為什麼?透過看原始碼恍然大悟

    #coding=utf-8
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    path =Service('D:\\dr\\chromedriver')
    driver = webdriver.Chrome(service=path)
    print(By.ID) 

    原始碼:

    class By:
        """
        Set of supported locator strategies.
        """
    
        ID = "id"
        XPATH = "xpath"
        LINK_TEXT = "link text"
        PARTIAL_LINK_TEXT = "partial link text"
        NAME = "name"
        TAG_NAME = "tag name"
        CLASS_NAME = "class name"
        CSS_SELECTOR = "css selector"

    嘗試替換id

    driver.find_element("id","dntest").send_keys("測試")

    作業:

    用getElementsByName給使用者名稱輸入“大牛測試”

    第二篇 專案實戰

    學習目標:

    • 理解專案需求
    • 測試用例編寫
    • 驗證碼識別與方法改良
    • 框架封裝
    • 無人值守方案解決思路

    1.1 功能測試用例

    ID

    模組名

    覆蓋功能點

    前置條件

    測試步驟

    預期結果

    testcase_01

    系統登入

    登入功能

    登入功能 正常

    (1)開啟系統登入頁面。

    (2)在使用者名稱和密碼輸入框 中分別輸入使用者名稱與密碼。

    (3)使用驗證碼識別技術識 別驗證碼。

    (4)輸入第(3)步中識別 出來的驗證碼。

    (5)單擊“登入”按鈕

    (1)登入頁面能正常開啟。

    (2)使用者名稱、密碼資訊能正常 輸入。

    (3)驗證碼能被正確識別。

    (4)識別出來的驗證碼能被正 確輸入。

    (5)使用者能正常登入系統

    testcase_02

    崗位管理

    崗位資訊管理 功能

    管理員能正常 登入系統

    (1)開啟崗位管理頁面。

    (2)單擊“新增”按鈕。

    (3)在新增崗位頁面,輸入 “崗位名稱”“崗位編碼”“顯 示順序”“備註”等。

    (4)單擊“確定”按鈕

    (1)崗位管理頁面能正常開啟。 (2)新增崗位頁面能正常開啟。 (3)測試步驟(3)中的這些字 段都能輸入相關欄位內容。

    (4)能夠成功新增崗位

    testcase_03

    部門管理

    部門資訊管理 功能

    管理員能正常 登入系統

    (1)開啟部門管理頁面。

    (2)單擊“新增”按鈕。

    (3)在新增部門頁面,輸入 “上級部門”“部門名稱”“顯 示順序”“負責人”“聯絡電 話”“郵箱”“部門狀態”(正 常或停用)等。

    (4)單擊“確定”按鈕

    (1)部門管理頁面能正常開啟。 (2)新增部門頁面能正常開啟。 (3)測試步驟(3)中的這些字 段都能輸入相關欄位內容。

    (4)能夠成功新增部門

    testcase_04

    角色管理

    角色資訊管理 功能

    管理員能正常 登入系統

    (1)開啟角色管理頁面。

    (2)單擊“新增”按鈕。

    (3)在新增角色頁面,輸入 “角色名稱”“許可權字元”“顯 示順序”“狀態”“備註”“菜 單許可權”等。

    (4)單擊“確定”按鈕

    (1)角色管理頁面能正常開啟。 (2)新增角色頁面能正常開啟。 (3)測試步驟(3)中的這些字 段都能輸入相關欄位內容。

    (4)能夠成功新增角色

    testcase_05

    使用者管理

    使用者資訊管理 功能

    管理員能正常 登入系統

    (1)開啟使用者管理頁面。

    (2)單擊“新增”按鈕。

    (3)在新增使用者頁面,輸入 “使用者名稱稱”“手機號碼”“登 錄賬戶”“使用者性別”“崗位” “角色”“歸屬部門”“郵箱” “登入密碼”“使用者狀態”等。

    (4)單擊“確定”按鈕

    (1)使用者管理頁面能正常開啟。 (2)新增使用者頁面能正常開啟。 (3)測試步驟(3)中的這些字 段都能輸入相關欄位內容,其 中崗位、角色和歸屬部門可以 選擇 testcase_02/03/04 新建的 記錄。

    (4)能夠成功新增使用者

    3.1 、專案部署

    1、專案本地部署

    • 先安裝JDK

    安裝後好輸入dos視窗輸入:java

    再輸入javac

    • #java -jar dntest.jar

    • 訪問網址:http://localhost/login

    使用者名稱/密碼 :

    3.2 、角色管理疑難分析

    角色管理-選中與不選中

    #side-menu > li.active > ul > li.active.selected > a

    新增-確定功能

    3.3、複雜控制元件定位

    #js複數定位

    3.4、登入驗證碼封裝

    斐斐打碼:

    http://www.fateadm.com/

    下載地址:

    原始碼修改:

    3.5、封裝

    學習目標:

    • 有、無返回值函式
    • unittest框架應用
    • unittest結合selenium實踐

    3.51、函式

    函式指將一組語句的集合透過一個名字(函式名)封裝起來,可以簡化程式碼、提高程式碼的複用性、程式碼可擴充套件

    Python函式有五大要素:def、函式名、函式體、引數、返回值

    例子:

    def sum(a,b):
        return a+b

    注:函式名“sum”和引數部分“a,b”,後面緊接一個冒號“:”可以理解為宣告函式結束標誌;“return a+b”是函式體

    例1:引數有初始值

    def testFunc2(a,b = 3):
       return a + b

    例2:可變引數-即函式中,引數的個數是不固定的,可以是1個,2個,還可以是0個等等

    def testFunc3(a,*b):
        print(a)
        print(b)
    testFunc3(2)

    例3:關鍵字引數

    關鍵字引數允許傳入0個或任意個含引數名的引數,而這些關鍵字引數在函式內部自動組裝成一個字典型別。例項原始碼如下:

    def testFunc4(id,name,**kw):
        print('id:',id,'name:',name,'other:',kw)
     
    testFunc4("a","b",ss='kk')

    引用外部函式:

      import xx
      from xx import xx

    3.52、 單元測試

    單元測試框架
    測試腳手架
    test fixture表示為了開展一項或多項測試所需要進行的準備工作,以及所有相關的清理操作。舉個例子,這可能包含建立臨時或代理的資料庫、目錄,再或者啟動一個伺服器程序。
    測試用例
    一個測試用例是一個獨立的測試單元。它檢查輸入特定的資料時的響應。unittest提供一個基類:TestCase,用於新建測試用例。
    測試套件
    test suite是一系列的測試用例,或測試套件,或兩者皆有。它用於歸檔需要一起執行的測試。
    測試執行器(test runner)
    test runner 是一個用於執行和輸出測試結果的元件。這個執行器可能使用圖形介面、文字介面,或返回一個特定的值表示執行測試的結果。
    斷言型別


    示例

    import unittest
    
    class TestStringMethods(unittest.TestCase):
    
        def test_upper(self):
            self.assertEqual('foo'.upper(), 'FOO')
    
        def test_isupper(self):
            self.assertTrue('FOO'.isupper())
            self.assertFalse('Foo'.isupper())
    
    if __name__ == '__main__':
        unittest.main()


    @unittest.skip(reason)
    跳過被此裝飾器裝飾的測試。 reason 為測試被跳過的原因。
    ●setUp
    ●tearDown

    3.53、批次執行指令碼:

    top_level_dir 路徑:第一次執行時如果為None 會取當前傳入的在路徑為 top_level_dir

    path = os.getcwd()+"\\cases"
    print(path)
    discover = unittest.defaultTestLoader.discover(path, pattern="test*.py", top_level_dir=None)
    runner = unittest.TextTestRunner()
    runner.run(discover)

    綜合案例:

    重構多視窗案例,增加斷言

    登入視窗

    需求:十組使用者登入批次登入功能

    問題:1、瀏覽器開啟兩遍

    2、預設值沒有去掉

    3、excel+ddt

    def get_data(filename, sheetnum):
        path = 'testdata.xls'
        book_data = xlrd.open_workbook(path)
        book_sheet = book_data.sheet_by_index(0)
        #book_sheet = book_data.sheet_by_index(1)  # 開啟檔案的中第一個表
        rows_num = book_sheet.nrows  # 行數
        rows0 = book_sheet.row_values(0)  # 第一行的各個名稱作為字典的鍵
        rows0_num = len(rows0)  # 這個可以知道有幾列
        list = []
    
        for i in range(1, rows_num):
            rows_data = book_sheet.row_values(i)  # 取每一行的值作為列表
            rows_dir = {}
            for y in range(0, rows0_num):  # 將每一列的值與每一行對應起來
                rows_dir[rows0[y]] = rows_data[y]
            list.append(rows_dir)
        return list

    3.6、無人值守自動化

    登入封裝

    複雜案例:迴圈登入實現

    3.7、框架封裝

    重構登入

    重構角色管理

    作業:

    1、完善需求表

    2、參照角色管理模組封裝方法,任選一模組進行封裝

    -------------------------------------------------------

    第四章 資料驅動框架

    4.1、檔案

    • CSV檔案:
    # import  csv
    # p='test.csv'
    # c=csv.reader(open(p,'r',encoding='utf-8'))
    # for cs in c:
    #     print(cs[1])
    
    • 文字檔案

    • 路徑操作

    亂碼問題:

    程式碼加入:

    4.2 、Excel用法

    • 讀取exel :xlrd
    pip install xlrd
    • 寫excel:xwt
    pip install xlrd

    注:xlrd 2.0.1版本不支援xlsx,需回退到 1.2.0

    4.3、Json與xml

    dump用於將dict型別的資料轉成str,並寫入到json檔案

    load用於從json檔案中讀取資料

    dumps用於將dict型別的資料轉成str,因為如果直接將dict型別的資料寫入json檔案中會發生報錯,因此在將資料寫入時需要用到該函式。

    loads用於將str型別的資料轉成dict

    import json
    
    a_dict = {'a': 1, "b": 'qw', '''c''': ['q', 'w'], 'd': '您好'}
    a_json = json.dumps(a_dict, ensure_ascii=False, indent=2)  # 縮排2個空格
    print(type(a_json))
    print(a_json)
    

    xml檔案:

    <?xml version="1.0" encoding="UTF-8"?>
    <users>
    	<user id ="1001">
    		<username>test1</username>
                                  <password>t123</password>
    	</user>
                  <user id="1002">
    		<username>test2</username>
    		<password>t234</password>
                  </user>
    </users>

    xml:

    import  xml.dom.minidom
    dom=xml.dom.minidom.parse("user.xml")
    root= dom.documentElement
    ls=root.getElementsByTagName("user")
    print(ls[1].getAttribute("id"))
    print(ls[1].getElementsByTagName("password")[0].childNodes[0].nodeValue)
    for l in ls:
        print(l.getElementsByTagName("password")[0].childNodes[0].nodeValue)

    4.4 、ddt框架

    安裝ddt庫

    pip install ddt 

    @符號用做函式的修飾符

    @data,先執行data內的屬性

    @data 跟for迴圈一樣 遍歷元組每個資料 然後傳遞給被裝飾的方法的一個引數,有幾條資料 就執行幾次用例

    如:

    @data(2, 3)

    @unpack() 分解資料,主要是把元組和列表解析成多個引數

    案例:迴圈登入功能實踐

    • 透過excel:

    需求格式

    [{"username": "tim", "password": "123"}, {"username": "tim1", "password": "123"}]
    • 透過json實現

    {"1":{"user":"dntest","passwd":"admin123"},
     "2":{"user":"dntest","passwd":"admin123"},
     "3":{"user":"dntest","passwd":"admin123"}
    }

    第五章 PageObject設計模式

    5.1 、測試報告:

    下載HTMLTestRunner.py檔案,下載地址:

    http://tungwaiyip.info/software/HTMLTestRunner.html

    它的語法是基於Python 2的。假如需要Python3版本的,需要對它進行修改

    HTMLTestRunner.py放於Python Lib目錄下

    5.2 、類與繼承

    5.3 po實戰

    PO概念:

    • 公共方法代表頁面提供的服務
    • 不要暴露頁面細節
    • 不要把斷言和操作細節混用
    • 方法可以 return 到新開啟的頁面
    • 不要把整頁內容都放到PO
    • 相同的行為會產生不同的結果,可以封裝不同結果

    例:以登入為例PageObject實現

    setup():測試函式執行前執行

    teardown():測試函式執行完後執行

    setUpClass():必須使用

    @classmethod 裝飾器,所有test執行前執行一

    tearDownClass():必須使用

    @classmethod裝飾器,所有test執行完後執行一次

    框架重構:

    1、基本類封裝改良

    可變引數

    *args是arguments單詞縮寫,表示任意多個無名引數,是一個tuple,如 (1,2,3,‘dn','test')

    2、logging模組

    log改良

    #pip install loguru

    log.debug('除錯訊息')

    log.info('普通訊息')

    log.warning('警告訊息')

    log.error('錯誤訊息')

    log.critical('嚴重錯誤訊息')

    log.success('成功呼叫')

    第六章 pytest重構專案

    6.1 pytest基礎

    單元測試框架pytest

    6.11 安裝:#pip install pytest

    檢視版本:#pytest --version

    第一個示例:

    def daniu(x):
        return x+1
    def test_answer():
        assert daniu(8) == 9

    注意:(1) 執行pycharm設定

    pycharm -> preferences -> Tools-> python integrated Tools

    (2) 命令列執行

    #pytest xx.py

    (3) 指定執行某個test方法

    pytest test_02.py::TestDemo::test01

    6.12 格式示例

    檔名以test_開頭或以_test結尾

    例:

    測試類以Test開頭,不能有init方法

    例:

    測試方法以test開頭

    例:

    6.13 pyest斷言:

    常用斷言:關鍵字assert + 表示式

    assert xx :判斷 xx 為真
    assert not xx :判斷 xx 不為真
    assert a in b :判斷 b 包含 a
    assert a == b :判斷 a 等於 b
    assert a != b :判斷 a 不等於 b

    6.14 執行命令:

    • -v增加顯示測試方法以及測試透過率百分比。

    #pytest -v xx.py

    if __name__ =="__main__":
        pytest.main(["-v","test_02.py"])

    • -k 篩查只執行部分測試用例,如test_02中test01,命令為:

    #pytest -k 01 test_02.py

    • -s 顯示測試程式碼中print語句

    6.2 測試前置與銷燬

    setup與teardown作用範圍同unittest框架

    • setup函式是可以在測試方法執行之前執行的
    • teardown函式是可以在測試方法執行之後執行的

    例子:

    • setup_class,作用範圍每個類之前
    • teardown_class,作用範圍每個類之後

    執行引數

    -x執行失敗停止執行

    #python -m pytest -v -x test_15.py

    引數 --maxfail=n 如 test_15.py 執行兩次2敗(即有個test是fail則停止)

    python -m pytest -v --maxfail=2 test_15.py

    --lf (last failed)重新執行上次失敗的用例

    #python -m pytest -v --lf test_15.py

    --ff(failed first) 執行所有用例,但先執行上次失敗的用例,

    #python -m pytest -v --ff test_15.py

    6.3 裝飾器

    裝飾器含義:

    fixture 自定義測試用例預置條件

    params:(list 型別)提供引數資料,供呼叫標記方法的函式使用

    fixture普通用法:

    例:test_fix_01.py

    autouse:是否自動執行,預設為 False 不執行,設定為 True 自動執行,無需每個函式注入fixture

    例:

    import pytest
    
    @pytest.fixture(autouse=True)
    def para():
        str = "daniutest"
        print(str)
    
    def test_01(para):
        print("大牛測試01")
    
    def test_02():
        #獲取para物件
        print("大牛測試02")
    
    def test_03():
        print("大牛測試03")
    
    

    fixture在類外部

    import pytest
    
    @pytest.fixture(scope="class",autouse=True)
    def para():
        str = "daniutest"
        print(str)
    class TestDaniu1:
        def test_01(self):
            print("大牛測試01")
    
        def test_02(self):
            #獲取para物件
            print("大牛測試02")
    class TestDaniu2:
        def test_03(self):
            print("大牛測試03")
    

    類使用fixture

    例:test_fix_04.py

    params引數使用:

    透過固定引數 request 傳遞引數

    例:test_fix_05.py

    多個引數,params = [''","",""]

    例:test_fix_06.py

    skip 跳過,標註功能暫未實現的或有問題測試用例

    例子:

    @pytest.mark.skip("not done!")
        def test01(self):
            print ("daniu")
            assert True
        def test02(self):
            assert False

    @pytest.mark.skipif(condition,reason) 滿足條件會跳過測試方法或類

     @pytest.mark.skipif(da="daniu",reason="not done!")
        def test01(self):
            print ("daniu")
            assert True

    @pytest.mark.xfail(reason='測試失敗') 標記失敗用例

    @pytest.mark.run(order)

    @pytest.mark.run(1) 數字越小越優先執行

    pytest.mark.repeat(2) 執行當前用例兩次,再執行下面用例。

    重複執行標記用例,安裝repeat

    用法:

    #pip install pytest-repeat

    執行:#python -m pytest -v test_14.py

    6.4 conftest

    conftest檔案是pytest特有的配置檔案,可以實現資料、引數、方法、函式的共享

    fixture中scope引數可以控制fixture的作用範圍,共有四種fuction、scope、module和session四種,具體如下。

    function:每一個函式和方法都會呼叫:

    例:conftest.py

    import pytest
    
    @pytest.fixture(scope="session")
    def auto():
        print("daniutest")

    class:每一個類呼叫一次

    module:每一個python檔案呼叫一次

    session:是多個檔案呼叫一次,修改檔案conftest引數為“session”

    6.5 引數化

    • parametrize引數化,第一個引數是字串;第2個引數是列表

    傳一個引數 多個值:

    import pytest
    
    @pytest.mark.parametrize("param",[10,2,3,4])
    def test_method(param):
        assert param > 3
    
    • 傳多個引數,多個值,值用元組形式
    #引數化,傳多個引數多個值
    import pytest
    
    @pytest.mark.parametrize("username,passwd",
                             [("daniu", "daniu123"), ("daniu1", "daniu123"), ("daniu2", "daniu123")])
    def test_login(username, passwd):
        print(username + " : " + passwd)
        assert username == passwd
    if __name__ =="__main__":
        pytest.main(["-s","test_07.py"])
    • 多個引數混用
    import pytest
    data1 = ['daniu','daniu1']
    data2 = ['xiaoniu','xiaoniu2']
    @pytest.mark.parametrize("a", data1)
    @pytest.mark.parametrize("b", data2)
    def test_para(a, b):
        print(a + " : " + b)
        # assert a == b
    if __name__ =="__main__":
        pytest.main(["-s","test_08.py"])
    • 傳入字典
    import pytest
    json=({"username":"daniu","passwd":"123456"},{"username":"daniu1","password":"123456"})
    @pytest.mark.parametrize('json', json)
    def test_parametrize_1(json):
        print(f'username : {json["username"]}, password : {json["password"]}')
    
    if __name__ =="__main__":
        pytest.main(["-s","test_09.py"])

    pytest提供了mark功能,可以給測試用例打上各種各樣的標籤,執行用例時可以指定執行某個標籤。mark功能作用就是靈活的管理和執行測試用例

    例:給類與方法打標籤

    # 可以給類打標籤
    @pytest.mark.daniu
    class TestDemo:
    
        @pytest.mark.xiaoniu
        @pytest.mark.daniu
        def test_login(self):
            pass
    
    if __name__ =="__main__":
        pytest.main(["-sv","test_10.py"])

    注:執行標記用例

    與標記集合

    #引數化,傳多個引數多個值
    import pytest
    
    @pytest.mark.parametrize("username,passwd",
                             [("daniu", "daniu123"), ("daniu1", "daniu123"),
                              pytest.param("daniu2", "daniu123", marks=pytest.mark.xfail)])
    def test_login(username, passwd):
        print(username + " : " + passwd)
        assert username == passwd
    if __name__ =="__main__":
        pytest.main(["-s","test_11.py"])

    6.6 整合html與Allure報告

    1、普通html報告

    #pip install pytest-html

    執行命令#pytest --html=report.html -sv test01.py

    報告:

    2、Allure報告

    https://allurereport.org/docs/pytest/

    mac系統配置。下載zip檔案

    #vi ~/.bash_profile

    export Maven_HOME=/Users/tim/Downloads/apache-maven-3.8.4
    export PATH=$PATH:$Maven_HOME/bin
    export PATH=$PATH:/Users/tim/Downloads/allure-2.25.0/bin
    export PATH

    #source ~/.bash_profile

    #allure --version

    執行命令:

    #pytest --alluredir=./allure_report -sv test_instance06.py 生成json文

    或#python -m pytest test_instance07.py --alluredir=./result/2

    #allure serve allure_report

    生成報告如下:

    法二、使用 allure generate 生成 html 格式的測試結果報告,並用allure open開啟

    #allure generate ./result/ -o ./result/3 --clean

    將 ./result/ 目錄下的測試資料生成 HTML 測試報告到 ./result/3路徑下

    #allure open -h 127.0.0.1 -p 8883 ./result/3

    開啟報告:

    或者,右鍵run->index.html 開啟報告

    #allure generate report\tmp -c -o report\allure-report

    report/tmp:每個用例的執行結果生成的每個json檔案存放的位置

    -o report/allure-report:allure報告生成的位置【指定目錄生成測試報告】

    -c report/allure-report:新的allure報告生成之前先把先前的allure報告清理掉

    allure標記描述

    • Overview:總覽
    • Categories:類別,預設是分了failed和error,歸類執行結果
    • Suites:測試套件,就是所有用例的層級關係,可以根據package、module、類、方法查用例
    • Graphs:測試結果圖形化,包括用例執行結果的分佈圖,優先順序,耗時等
    • Timeline:測試用例精確的測試時序(執行順序),包括執行時間
    • Behaviors:行為驅動,根據epic、feature、story來分組測試用例
    • Packages:按照package、module來分組測試用例

    EpicUser Story邏輯上的集合, 一個Epic可以被break down成多個小的User Story; 一個Epic可能需要多個Sprint才能完成

    Epic:指一個大型的、跨越多個迭代週期的使用者需求或者業務功能

    Feature:Epics有時包含著太多且模糊的需求,所以常常包含著不同的特性,而一個特性就是一組可以歸為一類的需求,就是一個Feature

    story:Epic 通常包含多個相關的故事(User Story),這些故事描述了 Epic 所包含的具體功能和需求,

    User Story 是一種簡潔、可理解、可驗證的描述方式,用於表示軟體系統的使用者需求和期望行為

    @allure.epic()

    敏捷中epic

    @allure.feature()

    模組

    @allure.story()

    使用者故事

    @allure.title(用例的標題)

    標題

    @allure.testcase()

    連結測試用例地址

    @allure.issue()

    bug,連結地址

    @allure.description()

    用例描述

    @allure.step()

    操作步驟

    @allure.severity()

    用例等級,blocker,critical,normal,minor,trivial

    @allure.link()

    定義一個連結在測試報告中展示

    @allure.attachment()

    報告新增附件

    嚴重程度(Severity)

    Blocker: 即系統無法執行、崩潰或嚴重資源不足、應用模組無法啟動或異常退出、無法測試、造成系統不穩定

    Critical:即影響系統功能或操作,主要功能存在嚴重缺陷,但不會影響到系統穩定性

    Major:即介面、效能缺陷、相容性

    Minor:不太重要

    Trivial:即易用性及建議性問題,不重要

    報告:

    • @allure.step() 只能以裝飾器的形式放在類或者方法上面
    • with allure.step(): 可以放在測試用例方法裡面,但測試步驟的程式碼需要被該語句包含

    在測試報告中新增圖片或影片

    在測試報告裡附加圖片

    allure.attach.file("image.png", attachment_type=allure.attachment_type.PNG)
    allure.attach.file("video.mp4", name="影片", \
     attachment_type=allure.attachment_type.MP4)

    修訂:https://blog.csdn.net/u010698107/article/details/111416173

    python 反射 https://blog.csdn.net/u010698107/article/details/117600196?spm=1001.2014.3001.5502

    6.7 配置檔案

    pytest.ini:pytest主配置檔案,可以改變pytest的預設行為,有很多可配置的選項,

    markers 作用:測試用例中新增了 @pytest.mark.smoke 裝飾器,如果不新增marks選項的話,就會報warnings

    #python -m pytest --markers

    例:

    import  pytest
    class TestDemo:
        @pytest.mark.smoke
        def test01(self):
            print ("daniu")
            assert True
        def test02(self):
            assert False
    
        @pytest.mark.get
        def test03(self):
            assert False
    if __name__ =="__main__":
        pytest.main(["-v","test_02.py"])

    執行:

    #python -m pytest -m get

    addopts = -rsxX -l --tb=short --strict

    --strict選項表示禁止使用未在ini檔案中註冊的標記

    #python -m pytest --strict-markers -m ge //.ini中沒有使用ge作標記

    指定不用訪問的目錄:

    norecursedirs = .* daniu *. egg dist build

    不執行daniu資料夾

    如:testpaths = TestCases

    python_classes = *Suite 會搜尋xxSuite測試類

    python_files更改預設的測試檔案搜尋規則

    如:新增daniu_xx.py檔案。

    python_files = daniu_*

    python_functions = niu_*

    def niu_04(self):
        assert False

    addopts引數可以更改預設命令列選項

    addopts可以更改預設命令列引數,將一些命令新增到pytest.ini裡則不需要每次命令列執行時都帶上引數,預設以pytest.ini裡配置去執行,多個命令列引數用空格分隔,可新增多個命令列引數

    pytest -v --rerun=2 --html=daniureport.html --self-contained-html -n=auto

    log_cli=True, 方便檢視package中module下測試用例是passed還是failed

    切換環境外掛

    #pip install pytest-base-url

    方式一、用命令列執行

    #python -m pytest -s --base-url http://www.baidu.com xx.py //傳遞一個url引數

    方式二、配置檔案pytest.ini

    方式三、

    • Hook函式的名稱是確定的;
    • pytest有非常多的鉤子函式;
    • 使用時直接編寫函式體;
    pytest測試用例的執行順序:
    • pytest_addoption:新增命令列引數,執行時先讀取命令列引數
    • pytest_collection_modifyitems:收集測試用例,收集之後改編碼,改執行順序
    • pytest_collection_finish:收集之後的操作
    • pytest_runtest_setup:在呼叫pytest_runtest_call之前呼叫
    • pytest_runtest_call:呼叫執行測試的用例
    • pytest_runtest_makereport:執行測試用例,返回setup,call,teardown的執行結果
    • 例:pytest_addoption傳遞引數
    • #python -m pytest -s --env http://www.baidu.com --res http://

    方式四、conftest結合ymal

    • #pip install PyYAML
    • 執行後

    #python -m pytest -s



    第七章 pytest框架搭建

    詳見影片課程:


    第八章 平臺搭建

    • selenium grid 多執行緒測試之selenium-server
    #java -jar selenium-server-4.1.2.jar standalone

    • 訪問:http://localhost:4444/

    • 環境移植

    pip freeze > requirements.txt
    • 安裝
    pip install -r requirements.txt

    平臺優勢

    1) 賬號管理

    2) 整合不同的環境

    3) 儲存每次執行結果

    4) 發郵件

    5) 定時執行

    .....

    Git提交異常:

    問題:

    commit加上a,a表示新增

    #git push -u origin master

    Jenkins報告亂碼:

    #System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

    問題思考:

    在Python中,列舉和我們在物件中定義的類變數時一樣的,每一個類變數就是一個列舉項,訪問列舉項的方式為:類名加上類變數

    注:每次作業傳送至2574674466@qq.com



相關文章