第一篇 基礎案例篇
前言:
● 內卷時代測試人員如何面對? 逃避 還是提高自己?
● 為什麼要學習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])
rangerange(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、原始碼中學習常用方法
- 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)} )
- click
--應用場景:單擊
click 方法用於實現單擊操作,如 button 登入操作,以 login.html 頁面登入為例,示例 程式碼如下:
driver.find_element(By.ID,"loginbtn").click()
- get_attribute
login.html頁面 class
-
- is_selected
-- 應用場景:常用於檢查選擇框狀態是否選中
返回值:True/False
login.html 頁面 男/女
print(driver.find_element(By.NAME,"checkbox1").is_selected()) print(driver.find_element(By.NAME,"checkbox2").is_selected())
- 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"]
- is_displayed
-- 應用場景:是否顯示,肉眼可見
返回值:True/False
print(driver.find_element(By.ID,"dntest").is_displayed)
- maximize_window
-- 最大化
driver.maximize_window()
- fullscreen_window
-- 全屏
driver.fullscreen_window()
- minimize_window
-- 最小化
driver.minimize_window()
- text
-- linkText("上傳附件")
driver.find_element(By.LINK_TEXT,"上傳資料頁面").get_attribute ('textContent') driver.find_element(By.LINK_TEXT,"上傳資料頁面").text
- 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)
- driver.back() 後退
#瀏覽器工具欄向後操作 driver.back();
- driver.forward() 前進
#瀏覽器工具欄向前操作 driver.forward();
- close()
關閉瀏覽器,多個tab只關閉當前。老版本driver不會退出
- quit
關閉並driver退出,多個tab一起關閉
- current_url
login.html頁面
print(driver.current_url)
- driver.current_window_handle
current_window_handle 方法用於返回視窗控制代碼,即標識視窗字串
driver.current_window_handle
- refresh
-- 重新整理
#重新整理當前頁面 driver.refresh()
- title
-- 如:login.html,title為"大牛測試"
#在控制檯上列印 title“大牛測試” print(driver.title)
- page_source
-- page_source 方法用於輸出網頁原始碼
print(driver.page_source)
- get_screenshot_as_base64()
圖片編碼線上轉換
https://tool.jisuapi.com/base642pic.html
- 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 ActionChainsdriver.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、等待時間
- 強制等待 time.sleep()
- 隱性等待driver.implicitly_wait()
設定的隱式等待時間為10秒
- 顯式(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
●tearDown3.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 不等於 b6.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來分組測試用例
Epic是User 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