python語言的selenium自動化功能測試
自動化工具介紹
- UFT 非開源
- RobotFramework
- 高度擴展
- 擁有大量的庫
- 支持關鍵字驅動
- Selenium
- 業內普及
- 靈活
- 使用場景廣泛
為什么要進行自動化測試?
- 目的
- 解決回歸測試、壓力測試、兼容性測試
- 提高測試效率保證產品質量
- 相關解釋
- 回歸測試:項目在發布新版本之前進行的功能驗證
- 壓力測試:可以理解多用戶去操作軟件,統計服務器處理用戶請求的能力
- 兼容性測試:表示在不同瀏覽器下軟件運行狀態
- 如下三種情況一般需要使用自動化?
- 需求變動不頻繁
- 項目周期長
- 項目需要回歸測試
配置編程環境
- window下安裝python3.7(傻瓜式安裝)
- Linux下安裝python3.7
- 安裝selenium包 pip3 install selenium
- 下載谷歌、火狐、IE驅動
- 安裝pycharm
selenium的使用
selenium安裝、卸載以及查看命令
- 安裝 pip install selenium
- 卸載 pip uninstall selenium
- 查看 pip show selenium
元素的定位
- id定位 find_element_by_id("kw") 注:有些id值動態變化
- class_name定位 find_element_by_class_name("s_ipt") 注:classname有可能重復
- tag_name定位 find_element_by_tag_name("input") 注:tagname最容易重復
- name定位 find_element_by_name("wd") 注:name有可能重復
- link文字精確定位 find_element_by_link_text("登錄")
- link文字模糊定位 find_element_by_partial_link_text("登")
- CSS定位(常用定位策略) 在selenium中推薦使用css定位,因為它比xpath定位速度快
- id選擇器 #id值
- class選擇器 .class值
- 元素選擇器 標簽名 >表示直接子級 空格表示子級
- p[id="user"]>input 表示p元素(id為user)的直接子級 或者使用p#user>input
- p[id="user"] input 表示p元素(id為user)的子級 或者使用p#user input
- css延伸
- input[type^="p"] type屬性以p字母開頭的元素
- input[type$="d"] type屬性以d結尾的元素
- input[type*="w"] type屬性包含w字母的元素
- XPath定位的常規策略
- 路徑
- 絕對路徑 絕對路徑對頁面結構要求非常高,不建議使用
- 相對路徑 匹配任意層級的元素,不限制元素的位置
- 相對路徑以//開始
- 格式 //input或者//*
- 路徑結合屬性 //input[@placeholder="密碼A"]
- 路徑結合邏輯(多個屬性)
- //input[@placeholder="密碼A" and @name="password"]
- //p[@id="parent"]/input[@placeholder="密碼A" and @name="password"
- 路徑結合層級
- xpath的延伸
- //*[text()="xxx"] 文本內容是xxx的元素
- //*[contains(@attribute,"xxx")] 屬性中含有xxx的元素
- //*[starts-weith(@attribute,"xxx")] 屬性以xxx開頭的元素
- 路徑
- 定位一組元素
- 通過下標操作定位元素 driver.find_elements_by_tag_name("input")[0].send_keys("吳鵬") 向一組input元素的第一個input輸入內容
- 通過遍歷操作定位元素 for el in elements:→el.send_keys("吳鵬")
元素操作方法
- 為什么要學習操作元素的方法?
- 需要讓腳本模擬用戶給指定元素輸入值
- 需要讓腳本模擬人為刪除元素的內容
- 需要讓腳本模擬點擊操作
- 相關元素的操作方法
- send_keys() 擴展可以實現上傳的操作(send_keys("文件路徑"))
- click()
- clear() 清空
- maximize_window() 窗口最大化
- set_window_size(width,height) 設置瀏覽器窗口大小
- set_window_position(x,y) 設置瀏覽器窗口位置
- back() 后退,模擬瀏覽器后天按鈕
- forward() 前進,模擬瀏覽器前進按鈕
- refresh() 刷新,模擬瀏覽器F5刷新
- close() 關閉當前窗口,模擬電機架瀏覽器關閉按鈕
- quit() 關閉瀏覽器驅動對象,關閉所有程序啟動的窗口
- title 獲取頁面title
- current_url 獲取當前頁面URL
獲取元素信息的常用方法
- size 返回元素大小
- text 返回元素的文本
- get_attribute("xxx") 獲取屬性值,傳遞的參數為元素的屬性名
- is_displayed() 判斷元素是否可見
- is_enabled() 判斷元素是否可用
- is_selected() 判斷元素是否選中,用來檢查復選框或單選按鈕是否被選中
鼠標操作
- 對於鼠標操作需要導入ActionChains包 from selenium.webdriver.common.action_chains import ActionChains
- 鼠標事件常用的操作方法 初始化 action=ActionChains(driver)
- context_click() 右擊 action.context_click(driver.find_element_by_css_selector("#user")).perform()
- selenium框架中雖然提供了右擊鼠標方法,但是沒有提供選擇右擊菜單方法,可以通過發送快捷鍵的方式解決(經過測試,谷歌瀏覽器不支持)
- double_click() 雙擊 action.double_click(driver.find_element_by_css_selector("#user").send_keys("admin")).perform()
- drag_and_drop() 拖拽 action.drag_and_drop(第一個元素,第二個元素).perform()
- action.drag_and_drop_by_offset(source,xoffset=360,yoffset=180).perform()
- move_to_element() 懸停 action.move_to_element(driver.find_element_by_css_selector("button")).perform()
- perform() 執行以上事件方法
- context_click() 右擊 action.context_click(driver.find_element_by_css_selector("#user")).perform()
鍵盤操作
selenium中把鍵盤的案件都封裝在keys類中,導包 from selenium.webdriver.common.keys import keys
- 刪除 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.BACK_SPACE) 單鍵就一個參數
- 全選 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"a") 組合鍵兩個參數
- 復制 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"c")
- 粘貼 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"v")
元素等待
元素等待類型
- 隱式等待
- 什么是隱式等待? 定位元素時,如果能定位到元素則直接返回該元素,不觸發等待,如果不能定位到該元素,則間隔一段時間再去定位如果超出,則拋出such**異常
- 方法 driver.implicitly_wait(timeout)
- 顯式等待
- 什么是顯式等待? 定位指定元素時,如果能定位到元素則直接返回該元素,不觸發等待,如果不能定位到元素,則隔一段時間再去定位,如果超出,跑出time**異常
- 實現方式
- 導包 from selenium.webdriver.support.wait import WebDriverWait
- 調用方法 until(method):直到...時 一般使用匿名函數來實現
- element=WebDriverWait(driver,10,1).until(lambda x:x.find_element_by_id("useA"))
操作API
下拉框
- 使用css去定位下拉框 如果option選項沒有value值得話,css定位或者其他定位就不太方便
- 使用Select from selenium.webdriver.support.select import Select
- 通過下標 Select(driver.find_element_by_css_selector("#userA")).select_by_index(1)
- 通過value值 select_by_value(" ")
- 通過顯示文本 select_by_visible_text(" ")
彈出框處理
- 網頁中常用的彈出框
- alert 警告框
- confirm 確認框
- prompt 提示框
- 彈出框的處理方法
- 獲取彈出框對象 alert=driver.switch_to.alert
- 調用
- alert.text 返回彈出的文字信息
- alert.accept() 接受對話框選項(同意)
- alert.dismiss() 取消對話框選項(取消)
滾動條操作
- 設置JavaScript腳本控制滾動條 js="window.scrollTo(0,1000)" 0表示左邊距,1000表示上邊距
- selenium調用執行JavaScript腳本的方法 driver.excute_script(js)
frame切換
- 什么是frame切換? 就是作用在當前頁面中指定區域顯示另一頁面元素
- 具體的使用步驟
- driver.switch_to.frame(frame_reference) 切換到指定frame的方法 frame_reference:可以為frame框架的name、id或者定位到的frame元素
- driver.switch_to.default_content() 恢復默認頁面方法
多窗口切換
- 出現過程 在HTML中,當點擊鏈接或者按鈕時,有的會在新的窗口打開頁面
- 為什么需要切換? 頁面存在多個窗口式,selenium默認焦點只會在主窗口上,不切換窗口,無法操作除主窗口以外的窗口內的元素
- 如何實現 在selenium中封裝了獲取當前窗口句柄,獲取所有窗口句柄和切換到指定句柄窗口的方法
- driver.current_windwo_handle 獲取當前窗口句柄
- driver.window_handles 獲取所有窗口句柄
- driver.switch_to.window(handle) 切換到指定句柄窗口
from selenium import webdriver
driver = webdriver.Chrome()
# 獲取當前句柄
current_handle = driver.current_window_handle
# 點擊跳轉之另一個頁面
driver.find_elements_by_id("link").click()
# 獲取所有句柄
handles = driver.window_handles
for handle in handles:
if handle != current_handle:
driver.switch_to.window(handle)
driver.find_elements_by_xpath("//span[contains(text(),'輸入')]")
窗口截圖
自動化腳本是由程序去執行的,因此有時候打印的錯誤信息並不是十分明確,如果在執行出錯的時候對當前窗口截圖保存,那么通過圖片就可以非常直觀的看到出錯的原因.
在selenium中,提供了截圖的方法,只需要調用即可.方法:driver.get_screenshot_as_file(imgpath) imgpath表示圖片保存路徑
驗證碼的處理方式
- 去掉驗證碼 測試環境下采用
- 設置萬能驗證碼 生產環境和測試環境下采用
- 驗證碼識別技術 通過python-tesseract來識別圖片類型驗證碼:識別率很難達到100%
- 記錄cookie 通過記錄cookie進行跳過登錄
- Cookie是由web服務器生成的,並且保存在用戶瀏覽器上的小文本文件,它可以包含用戶相關的信息.
- Cookie數據格式:鍵值對組成(python中的字典)
- Cookie產生:客戶端請求服務器,如果服務器需要記錄該用戶狀態,就向客戶端瀏覽器頒發一個Cookie數據
- Cookie使用:當瀏覽器再次請求該網站時,瀏覽器把請求的數據和Cookie數據一同提交給服務器,服務器檢查該Cookie來辨認用戶狀態
- cookie的使用 selenium中對cookie操作提供相應的方法
- get_cookie(name) 獲取指定cookie name為cookie的名稱
- get_cookies() 獲取本網站所有本地cookies
- add_cookie(cookie_dict) 添加cookie cookie_dict:一個字典對象,必選的鍵包括:"name"and"value"
- 步驟
- 打開百度url driver.get("https://www.baidu.com")
- 設置cookie信息 dirver.add_cookie({"name":"BDUSS","value":"根據實際情況編寫值"})
UnitTest框架的使用
unitTest核心要素
- TestCase (測試用例)
- TestSuite (測試套件)
- TestRunner (以文本的形式運行測試用例)
- TestLoader (批量執行測試用例-搜索指定文件夾內指定字母開頭的模式)[推薦]
- Fixture (固定裝置 兩個固定的函數,一個初始化時使用,一個結束時使用)
定義測試用例
- 導包 import unittest
- 定義測試類 新建測試類必須繼承unittest.TestCase
- 定義測試方法:測試方法名稱命名必須以test開頭
測試套件TestSuite
- 實例化 suite=unittest.TestSuite()
- 添加用例 suite.addTest(ClassName("MethodName")) ClassName為類名,MethodName為方法名
- 添加擴展 suite.addTest(unittest.makeSuite(ClassName)) 搜索指定ClassName內test開頭的方法並添加到測試套件中
- 執行測試套件中添加的用例(在2后使用)
- 實例化后去執行套件對象 runner=unittest.TextTestRunner()
- 調用run方法去執行 runner.run(suite)
TestLoader的用法
- 導包 import unittest
- 創建套件(一個目錄下的所有文件代表一個套件) suite=unittest.TestLoader().discover("../cases",pattern="test*.py") 不寫pattern屬性,表示一個目錄下的所有py文件
- 創建套件(用法跟TestLoader類似) suite=unittest.defaultTestLoader.discover("../cases",pattern="test*.py")
- 執行套件 unittest.TextTestRunner().run(suite)
Fixture的使用
其實就是兩個函數,這個函數可以一起使用,也可以單獨使用
函數級別
- 初始化函數 def setup()
- 結束函數 def tearDown()
類級別
- 開始函數 def setupClass()
- 結束函數 def teardownClass()
模塊級別(了解)
- 開始函數 def setupModule()
- 結束函數 def teardownModule()
元素定位的另一種編寫方式
- 導入By類 from selenium.webdriver.common.by import By
- 使用By類的方法 driver.find_element(By.ID,"#user").send_keys("name")
unittest的常用斷言
在用戶編寫的每個測試方法中都必須有對結果正確與否的判斷,否則就不是一個合格的單元測試方法,而unittest為用戶提供了足夠多的斷言方法,需要拋出異常添加raise
import unittest
class TestToBeTest(unittest.TestCase):
def test_assertEqual(self):
try:
a, b = 100, 200
sum = 300
self.assertEqual(a + b, sum, "斷言失敗") # 如果a+b==sum,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertNotEqual(self):
try:
a, b = 100, 200
sum = 300
self.assertNotEqual(a + b, sum, "斷言失敗") # 如果a+b=!sum,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertTrue(self):
try:
a, b = 100, 200
sum = 300
self.assertTrue(a + b == sum, "表達式不對") # 如果是true,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertFalse(self):
try:
a, b = 100, 200
sum = 300
self.assertFalse(a + b == sum, "表達式錯誤") # 如果是false,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertIS(self):
try:
a, b = 100, 200
self.assertIs(a, b, "表達式錯誤") # 如果a是b,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertNotIs(self):
try:
a, b = 100, 100
self.assertIsNot(a, b, "表達式錯誤") # 如果a不是b,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_assertLn(self):
try:
a, b = "100", "100"
self.assertIn(a, b, "表達式錯誤") # 如果a在b中,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_asserNotLn(self):
try:
a, b = "100", "100"
self.assertNotIn(a, b, "表達式錯誤") # 如果a不在b中,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_asserIsInstance(self):
try:
a, b = object, object
self.assertIsInstance(a, b, "表達式錯誤") # 如果a是b的類型,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
def test_asserNotIsInstance(self):
try:
a, b = str, object
self.assertNotIsInstance(a, b, "表達式錯誤") # 如果a不是b的類型,就執行通過,否則斷言失敗
except AssertionError as e:
print(e)
if __name__ == '__main__':
unittest.main(verbosity=2)
參數化應用
- 什么是參數化? 通過參數的方式來傳遞數據,從而實現數據和腳本分立,並且可以實現用例的重復執行.unittest測試框架,本身不支持參數化,但是可以通過安裝unittest擴展插件parameterized來實現
- 安裝
- pip install parameterized
- pyCharm安裝方式 File→setting→project
- 插件的應用
- 導包 from parameterized import parameterized
- 修飾測試函數 @parameterized.expend([數據])
- 數據格式 單個參數類型為列表,多個參數類型為列表嵌套元祖
- 在測試函數中的參數設置變量引用參數值,注意:變量的數量必須和數據值得個數相同
def get_data():
return [(1,2,3),(1,2,3),(1,2,3)]
class Test01(unittest.TestCase):
@parameterized.expend(get_data())
def test_add(self,a,b,result):
sum=add(a,b)
assert==result
print(result)
unittest的跳過
- 解釋 對於一些未完成的或者不滿足測試條件的測試函數和測試類,可以跳過執行.
- 使用方式
- 直接將測試函數標記成跳過 @unittest.skip('代碼未完成')
- 根據條件判斷測試函數是否跳過 @unittest.skipIf(condition,reason)
with與with open區別
- 共同點 打開文件
- 區別 with open是執行打開與關閉的操作一起
with open("../report/text.txt","w",encoding="utf-8") as f:
f.read()
PO模式→V3
po頁面層
# 用來封裝對象----用戶名、密碼、登錄等
from selenium import webdriver
class PageLogin:
def __init__(self):
self.driver = webdriver.Firefox()
self.driver.maximize_window()
self.driver.implicitly_wait(5)
self.driver.get("https://www.baidu.com")
# 點擊登錄 連接
def page_click_login_link(self):
pass
# 輸入用戶名
def page_input_username(self,username):
pass
# 輸入密碼
def page_input_pwd(self,password):
pass
# 輸入驗證碼
def page_input_code(self,code):
pass
# 點擊登錄信息
def page_click_login_button(self):
pass
# 獲取異常提示信息
def page_get_text(self):
pass
# 點擊提示框確定按鈕
def page_click_err_btn_ok(self):
pass
def page_login(self,username,password,code):
self.page_click_login_link()
self.page_input_username(username)
self.page_input_pwd(password)
self.page_input_code(code)
self.page_click_login_button()
po業務層
from parameterized import parameterized
from page.page_login import PageLogin
import unittest
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.login = PageLogin()
def tearDown(self) -> None:
self.login.driver.quit()
@parameterized.expand([("17327767735", "123456", "8888", "賬號不存在")])
def test_login(self, username, password, code, expect):
self.login.page_login(username, password, code)
msg = self.login.page_get_text()
try:
self.assertEqual(msg, expect)
self.login.page_click_err_btn_ok()
except AssertionError:
# 存放截圖代碼
pass
po模式→v4
- base(基類) page頁面一些公共的方法
- 初始化方法
- 查找元素方法
- 點擊元素方法
- 輸入方法
- 獲取文本方法
- 截圖方法
- page(頁面對象) 一個頁面封裝成一個對象,繼承base類
- 一個頁面封裝一個對象
- 將每個元素操作單獨封裝一個操作方法
- 組裝,根據需求組裝封裝的操作方法
- scripts(業務層) 導包調用page頁面
- 在unittest框架中不能使用def__init__()初始化方法
- 初始化方法 setup()
- 結束方法 teardown()
- 測試方法 根據要操作的業務來實現
擴展 loc變量:類型為元祖 *loc為解包
base文件夾下 base.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
class Base:
def __init__(self):
# 臨時代替driver
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://192.168.1.105:8080/KBMS/")
# 查找元素方法
def base_find_element(self, loc, timeout=30, poll_frequency=0.5):
return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(lambda x: x.find_element(*loc))
# 點擊方法
def base_click(self, loc):
self.base_find_element(loc).click()
# 輸入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
el.clear()
el.send_keys(value)
# 獲取文本方法
def base_get_text(self, loc):
return self.base_find_element(loc).text
# 獲取截圖
def base_get_image(self):
self.driver.get_screenshot_as_file("../image/{}.png" .format(time.strftime("%Y_%m_%d %H_$M_%S ")))page文件夾下 __init__.py(多用於放元素屬性) page_login.py
from selenium.webdriver.common.by import By
# # 登錄鏈接
# login_link = By.PARTIAL_LINK_TEXT, "登錄"
# 用戶名
login_username = By.ID, "username"
# 密碼
login_password = By.ID, "password"
# 登錄按鈕
login_btn = By.ID, "Layer1"
# 獲取異常文本信息
login_err_info = By.CSS_SELECTOR, "layui-layer"from base.base import Base
import page
class PageLogin(Base):
# # 點擊登錄鏈接
# def page_click_login_link(self):
# self.base_click(page.login_link)
# 輸入用戶名
def page_input_username(self, username):
self.base_input(page.login_username, username)
# 輸入用戶密碼
def page_input_password(self, password):
self.base_input(page.login_password, password)
# 點擊登錄
def page_click_login_btn(self):
self.base_click(page.login_btn)
# 組合業務方法
def page_login(self, username, password):
# self.page_click_login_link()
self.page_input_username(username)
self.page_input_password(password)
self.page_click_login_btn()scripts文件夾下 test_login.py
import unittest
from page.page_login import PageLogin
from parameterized import parameterized
import time
def get_data():
return [("admin", "admin")]
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.pageLogin = PageLogin()
def tearDown(self) -> None:
self.pageLogin.driver.quit()
@parameterized.expand(get_data())
def test_login(self, username, password):
self.pageLogin.page_login(username, password)
time.sleep(3)
if __name__ == '__main__':
unittest.main()
數據驅動
- 什么是數據驅動? 以數據來驅動整個測試用例的執行,也就是測試數據決定測試結果
- 數據驅動的特點?
- 可以把數據驅動理解為一種模式或者一種思想
- 數據驅動技術可以將用戶把關注點放在對測試數據的構建和維護上,而不是直接維護腳本,可以利用同樣的過程對不同的數據輸入進行測試。
- 數據驅動的實線要依賴參數化的技術
- 傳入數據的方式
- 直接定義在測試腳本中(簡單直觀,但代碼和數據未實現真正的分離,不方便后期維護)
- 從文件讀取數據,如JSON、excel、xml、txt等格式文件
- 從數據中讀取數據
- 直接調用接口獲取數據源
- 本地封裝一些生成數據的方法
JSON操作
- json的特點
- json是純文本
- json具有良好的自我描述性,便於閱讀和編寫
- json具有清晰的層級結構
- 有效的提升網絡傳輸效率
- json的語法規則
- 大括號保存對象
- 中括號保存數組
- 對象數組可以相互嵌套
- 數據采用鍵值對表示
- 多個數據由逗號分隔
- json的值
- 數字(整數或浮點數)
- 字符串(在雙引號中)
- 邏輯值(true或false)
- 數組(在中括號中)
- 對象(在大括號中)
- null
python字典與Json之間的轉換
python字典轉換為JSON字符串
import json
date = {
'id': 1,
'name': 'wupeng',
'school': None
}
json_str = json.dumps(date)
print(json_str)JSON字符串轉換為python字典
import json
json_str = '{"id": 1,"name": "wupeng","school":null}'
python_str = json.loads(json_str)
print(python_str)
JSON的寫入與讀取
寫入json文件
import json
data = {'name': 'wupeng', 'age': 18, 'school': None}
with open("./data/test.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False) # 第三個參數ascii碼,改False處理中文讀取json文件
import json
with open("./data/test.json", "r", encoding="utf-8") as f:
text = json.load(f)
print(text)
po模式→v5
- base
- - base.py(封裝元素)
- - get_driver.py(封裝dirver)
- data
- - data.json(存放參數化數據,json文件)
- - data.txt(存放參數化數據,txt文件)
- page
- - __init__.py(存放css、id等八大定位值)
- - page.clac.py(存放業務,以及組裝業務)
- scripts
- - test.clac.py(存放測試腳本)
- tools
- - read_json.py(讀取json文件,並獲取值)
- - read_txt.py(讀取txt文件,並獲取值)
base.py
from selenium.webdriver.support.wait import WebDriverWait
import time
class Base:
def __init__(self, driver):
self.driver = driver
# 查找元素方法
def base_find_element(self, loc, timeout=30, poll_frequency=0.5):
return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
lambda x: x.find_element(*loc))
# 點擊方法
def base_click(self, loc):
self.base_find_element(loc).click()
# 輸入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
el.clear()
el.send_keys(value)
# 獲取文本方法
def base_get_text(self, loc):
return self.base_find_element(loc).text
# 獲取value的屬性方法
def base_get_value(self, loc):
# 使用get_attribute獲取指定的元素值
self.base_find_element(loc).get_attribute("value")
# 獲取截圖
def base_get_image(self):
self.driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))get_driver.py
from selenium import webdriver
import page
class GetDriver:
driver = None
@classmethod
def get_driver(cls):
if cls.driver is None:
cls.driver = webdriver.Firefox()
cls.driver.maximize_window()
cls.driver.get(page.url)
return cls.driver
@classmethod
def quit_driver(cls):
if cls.driver:
cls.driver.quit()
# 一個坑,需要做dirver歸Num,實現單例
cls.driver = Nonedata.json
{
"data1": {
"a": 1,
"b": 2
},
"data2": {
"a": 1234,
"b": 1232
},
"data3": {
"a": 1234,
"b": 1232
}
}data.txt
1,2
3,4
5,6__init__.py
from selenium.webdriver.common.by import By
url = "http://cal.apple886.com/"
clac_num = By.CSS_SELECTOR, "#simple9"
# 加號
clac_add = By.CSS_SELECTOR, "#simpleAdd"
# 等號
clac_eq = By.CSS_SELECTOR, "#simpleEqual"
# 結果
clac_result = By.CSS_SELECTOR, "#resultIpt"
# 清屏
clac_clear = By.CSS_SELECTOR, "#simpleClearAllBtn"page_clac.py
from selenium.webdriver.common.by import By
import page
from base.base import Base
class PageClac(Base):
# 點擊數字
num = 12
def page_click_num(self, num):
for n in str(num):
loc = By.CSS_SELECTOR, "#simple{}".format(n)
self.base_click(loc)
# self.base_click(page.clac_num)
# 點擊加號
def page_click_add(self):
self.base_click(page.clac_add)
# 點擊等號
def page_click_eq(self):
self.base_click(page.clac_eq)
# 獲取結果方法
def page_click_result(self):
self.base_click(page.clac_result)
# 點擊清屏
def page_click_clear(self):
self.base_click(page.clac_clear)
# 組裝業務方法
def page_add_clac(self, a, b):
self.page_click_num(a)
self.page_click_add()
self.page_click_num(b)
self.page_click_eq()test_clac.py
import unittest
from page.page_clac import PageClac
from parameterized import parameterized
from tools.read_txt import read_txt
from base.get_driver import GetDriver
class TestClac(unittest.TestCase):
driver = None
@classmethod
def setUpClass(cls) -> None:
cls.driver = GetDriver().get_driver()
cls.clac = PageClac(cls.driver)
@classmethod
def tearDownClass(cls) -> None:
GetDriver().quit_driver()
@parameterized.expand(read_txt("data.txt"))
def test_add_clac(self, a, b):
self.clac.page_add_clac(a, b)read_json.py
import json
def read_json(fileName):
filepath = "../data/" + fileName
arr = []
with open(filepath, "r", encoding="utf-8") as file:
datas = json.load(file)
for data in datas.values():
arr.append((data['a'], data['b']))
return arrread_txt.py
def read_txt(fileName):
filepath = "../data/" + fileName
arr = []
with open(filepath, "r", encoding="utf-8") as file:
datas = file.readlines()
for data in datas:
arr.append(tuple(data.strip().split(",")))
return arr
日志特點及級別
元素的常用操作方法
# 清除文本
driver.find_element_by_id("#id").clear()
# 文本寫入
driver.find_element_by_id("#id").send_keys()
# 點擊
driver.find_element_by_id("#id").click()
# 獲取class元素的屬性
driver.find_element_by_id("#id").get_attribute("class")
# 獲取輸入框里面的文字
driver.find_element_by_id("#id").get_attribute("value")
瀏覽器的常用方法
# 模擬瀏覽器最大化
driver.maximize_window()
# 設置指定的瀏覽器的大小
driver.set_window_size(100,200)
# 模擬瀏覽器的位置
driver.set_window_position(100,200)
# 模擬瀏覽器的后退
driver.back()
# 模擬瀏覽器的前進
driver.forward()
# 模擬瀏覽器關閉按鈕(關閉單個窗口)
driver.refresh()
# 關閉所有webdriver啟動的窗口
driver.close()
driver.quit()
webdriver的其他方法
# 獲取用戶名文本框大小
driver.find_element_by_id("#id").size
# 獲取文本值
driver.find_element_by_id("#id").text
# 獲取元素屬性值
driver.find_element_by_id("#id").get_attribute("href")
# 獲取當前頁面title
driver.title
# 獲取當前頁面url
driver.current_url
# 判斷span元素是否顯示
driver.find_element_by_id("#id").is_displayed()
# 判斷取消按鈕是否可用
driver.find_element_by_id("#id").is_enabled()
webdriver操作鼠標
from selenium.webdriver.common.action_chains import ActionChains
# 使用鍵盤操作需要導入
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("https://www.baidu.com/")
# 指定元素鼠標右鍵
ActionChains(driver).context_click(driver.find_element_by_id("su")).perform()
# 指定元素鼠標雙擊
ActionChains(driver).double_click(driver.find_element_by_id("su")).perform()
# 拖動
box1=driver.find_element_by_id("box1")
box2=driver.find_element_by_id("box2")
# 將box1拖動到box2的位置
ActionChains(driver).drag_and_drop(box1,box2).perform()
# 將box1拖動到坐標500,0的位置
ActionChains(driver).drag_and_drop_by_offset(box1,500,0).perform()
# 鼠標懸停(比較經典的就是懸浮框)
ActionChains(driver).move_to_element(driver.find_element_by_id("su")).perform()
# 刪除鍵
driver.find_element_by_id("su").send_keys(Keys.BACK_SPACE)
# 空格鍵
driver.find_element_by_id("su").send_keys(Keys.SPACE)
# tab鍵
driver.find_element_by_id("su").send_keys(Keys.TAB)
# 回退鍵
driver.find_element_by_id("su").send_keys(Keys.ESCAPE)
# 回車鍵
driver.find_element_by_id("su").send_keys(Keys.ENTER)
# 全選
driver.find_element_by_id("su").send_keys(Keys.CONTROL,"c")
# 復制
driver.find_element_by_id("su").send_keys(Keys.CONTROL,"v")
元素等待(主要隱式等待)
調用方法:
driver.implicitly_wait(10)
說明:如果定位某一元素定位失敗,那么就會觸發隱式等待有效時長,如果在指定時長內加載完畢,則繼續執行,否則跑出NoSuchElementException異常,如果元素在第一次就定位到則不會觸發隱式等待時長.
文件上傳
待續----
下拉框
select的相關方法:
- select_by_index() 根據option索引來定位,從0開始
- select_by_value() 根據option屬性value值來定位
- select_by_visible_text() 根據option顯示文本來定位
代碼演示:(需要導入select的包)
from selenium import webdriver
from selenium.webdriver.support.select import Select
import time
driver=webdriver.Chrome()
driver.get("https://yqmbqldc.mazhoudao.com/investigation/index01.html")
driver.find_element_by_xpath('/html/body/div/div[2]/label').click()
time.sleep(3)
driver.find_element_by_xpath('/html/body/div/a').click()
time.sleep(3)
driver.find_element_by_xpath('/html/body/div/div/div[1]/a').click()
time.sleep(3)
select=driver.find_element_by_id("whcd")
Select(select).select_by_index(1)
time.sleep(3)
frame切換
使用WebDriver對象swith_to屬性
wd.switch_to.frame("屬性值") 屬性值可以是id的屬性,也可以是name的屬性
如果沒有id跟name,可以根據frame的元素位置或者屬性特征,使用find系列的方法
wd.switch_to.frame(wd.find_element_by_tag_name("iframe"))
警告框
HTML中常用的對話框有三種,處理的方法都大同小異
- alert
- confirm
- prompt
處理方法:
- text 返回alert等文字信息
- accept 接受對話框選項
- dismiss 取消對話框選項
示例代碼:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("F:\\Users\\R\\untitled2\\thname.html")
driver.find_element_by_id("btn").click()
time.sleep(2)
alert=driver._switch_to.alert.text
print(alert)
滾動條操作
為什么需要滾動條的操作
- webDriver類庫中並沒有直接提供對滾動條進行操作方法啊,但是它提供了可調用JavaScript腳本的方法,所以我們可以通過JavaScript腳本來達到操作滾動條的目的
- 滾動條一種可控制程序顯示范圍的組件.
示例代碼:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("F:\\Users\\R\\untitled2\\thname.html")
js1="window.scrollTo(0,0)"
js2="window.scrollTo(0,10000)"
time.sleep(3)
# 執行最底層
driver.execute_script(js2)
# 執行最頂層
driver.execute_script(js1)
切換表單方法
# 切換到frame子頁面 browser.switch_to.frame('iframeResult') time.sleep(3) browser.find_element_by_xpath('/html/body/a').click() #browser.find_element_by_link_text('這是一個鏈接使用了 href 屬性').click() time.sleep(3) # 切回父頁面(必須執行) browser.switch_to.default_content() #browser.find_element_by_css_selector('html body a').click() #time.sleep(3) browser.quit()
多窗口切換
在webDriver中封裝了獲取當前窗口句柄方法和獲取所有窗口句柄的方法以及切換指定句柄窗口的方法
方法:
- driver.current_window_handle 獲取當前窗口句柄
- driver.window_handles 獲取所有窗口句柄
- driver.switch_to.window(handle) 切換指定句柄窗口
示例代碼:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("https://www.baidu.com")
# 獲取當前句柄
handle=driver.current_window_handle
driver.find_element_by_id("jgwab").click()
time.sleep(3)
handles=driver.window_handles
for hand in handles:
if hand!=handle:
driver.switch_to.window(hand)
driver.find_element_by_xpath('/html/body/div[1]/div[1]/div/div/div/input').send_keys("犯罪")
time.sleep(3)
# 使用如下的網頁返回,可以退到原來的網頁
driver.switch_to.window(handle)
窗口截圖
說明:在webDriver類庫中,提供了截圖方法,我們只需要調用即可
方法:get_screenshot_as_file(imgpath) 截取當前窗口
代碼實例(一般出現在提交或者容易報錯的地方進行截圖)
driver.get_screenshot_as_file("./imgages.png")
驗證碼的處理
在web應用中,大部分系統在用戶登錄的時候都要求輸入雅正嗎,而我們在設計自動化腳本的時候,就需要面臨這驗證碼的問題
所以出現了如下的解決方式:
- 去掉驗證碼(測試環境下--采用)
- 設置萬能驗證碼(生產環境下--采用)
- 驗證碼識別技術(通過Python-tesseract來識別圖片類型驗證碼,識別率很難達到100%)
- 記錄cookie(通過記錄cookie進行登錄--推薦)
cookie
cookie是什么?
- cookie是一小段的文本信息,格式是python中的字段(鍵值對組成)
- cookie產生:客戶端請求服務器,如果服務器需要記錄該用戶狀態,就像客戶端瀏覽器辦法一個cookie格式
- cookie使用:當瀏覽器在請求該網站時,瀏覽器吧請求的網址連同該cookie一同提交給服務器,服務器檢查該cookie,以此來辨認用戶狀態.
為什么要記錄cookie?
- 用戶第一次登陸時,勾選下次直接登錄或者記住密碼,就是采用記錄cookie實現的
- cookie內記錄用戶名和密碼(加密)信息,之喲啊請求時服務器收到cookie,就識別成功,默認已登錄
記錄cookie,方法如下:
- get_cookie(name),name為鍵名 獲取指定cookie
- get_cookies() 獲取本網站所有本地cookies
- add_cookies(str),str為python中的字典格式 添加cookie
代碼實例(需要在火狐中尋找cookie中BAIDUID與BDUSS的鍵值對值)
from selenium import webdriver
import time
driver=webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.add_cookie({"name":"BAIDUID","value":"813F26D2B40131AA64B1C59546F4FA88:FG=1"})
driver.add_cookie({"name":"BDUSS","value":"N2cnRuQXEydGxIbnVIWVotSk11WkVpcjI1VnAxeU5WeFVrcktuNEhncGFUWHhlSVFBQUFBJCQAAAAAAAAAAAEAAABChzrXxOPOtNT4zfy8xwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFrAVF5awFReWV"})
time.sleep(3)
driver.refresh()
time.sleep(2)
UnitTest框架
好處
- 能夠組織多個用例去執行
- 提供豐富的斷言方法
- 提供豐富的日志與測試結果
核心要素
- TestCase
- TestSuite
- TextTestRunner
- Fixture
TestCase的使用(測試方法名稱命名必須以test開頭,原因是unittest.TestCase批量運行的方法是搜索執行test開頭的方法)
- 導包:import unittest 導入unittest框架
- 繼承:unittest.TestCase 新建測試類繼承unittest.TestCase
UnitTest斷言
斷言方式(主要的兩種)
- assertEqual(arg1,arg2,msg=None) 驗證arg=arg2,不等則fail(掌握)
- assertIn(arg1,arg2,msg=None) 驗證arg1是arg2的子串,不是則fail
改良版全功能自動化用例
# 登錄模塊 from selenium import webdriver import unittest import time import csv class Login(unittest.TestCase): def setUp(self) -> None: self.browser=webdriver.Chrome() self.browser.get("http://192.168.159.1:8082/") time.sleep(3) def tearDown(self) -> None: self.browser.quit() # 定義登錄帶參函數 def login(self,username,password): self.browser.find_element_by_xpath("//*[@id='shop_header']/div[2]/ul/li[7]/a").click() time.sleep(3) self.browser.find_element_by_id("user_name").send_keys(username) self.browser.find_element_by_id("user_password").send_keys(password) self.browser.find_element_by_xpath("//*[@id='login_form']/div[4]/div/button[1]").click() time.sleep(3) # 定義退出函數 def loginOut(self): self.browser.find_element_by_link_text("退出").click() # 登錄成功測試 def testLoginSuccess(self): self.login("wupeng","123456") # 用戶名為1測試 def testUserNull(self): self.login("","123456") # self.assertTrue(self.browser.find_element_by_xpath("//*[@id='login_form']/div[2]/div/label").text=="請輸入會員登錄名稱!") info=self.browser.find_element_by_xpath("//*[@id='login_form']/div[2]/div/label").text self.assertIn("請輸入會員登錄名稱",info) if __name__ == '__main__': unittest.main(verbosity=2) # 搜索模塊 from login import dbSlogin import unittest import time class Search(unittest.TestCase): myself=dbSlogin.Login myself.setUp(myself) myself.login(myself,"wupeng","123456") # 輸入正確搜索字段搜索 def testSearch(self): self.myself.browser.find_element_by_xpath("//*[@id='shop_top_search']/form/div/input").send_keys("蘋果") self.myself.browser.find_element_by_xpath("//*[@id='shop_top_search']/form/div/button").click() time.sleep(3) self.myself.tearDown(self.myself) if __name__ == '__main__': unittest.main(verbosity=2)
套件測試多條用例(最終所有用例寫完執行套件即可)
示例代碼:
import unittest
import time
from login import HTMLTestReport
import os
#生成報告的時間
current_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
# 用例路徑 默認獲取當前目錄
case_path = os.getcwd()
# 報告存放路徑 默認獲取當前目錄
report_path = os.path.join(os.getcwd(), 'DBShop_'+current_time+".html")
def all_case():
# test*.py 用例的文件必須是test開頭 並且只能包含字母或者下划線,否則有可能識別不了,無法全部執行
discover = unittest.defaultTestLoader.discover(case_path, pattern="ts*.py", top_level_dir=None)
print(discover)
return discover
if __name__ == "__main__":
fp = open(report_path, "wb")
# 用例標題,執行者可以自己更改
runner = HTMLTestReport.HTMLTestRunner(stream=fp, title="自動化測試報告", description='自動化測試報告', tester='wupeng')
runner.run(all_case())
fp.close()
C:\其他