前篇文章中,我們簡單介紹了部分WinApp自動化測試腳本常規操作,今天我們來講剩余的部分。
文件批量上傳
文件批量上傳和文件單個上傳原理是相同的,單個上傳直接傳入文件路徑即可,批量上傳需要進入批量上傳的文件所在目錄,然后觀察選中多個文件時【文件路徑輸入框】讀取的批量文件寫入規則,如圖7-12所示,可以看到規則是:“file_name” “file_name”。
我們只需要根據規則將批量文件組合成字符串輸入到【文件路徑輸入框】中,然后點擊【插入】即可完成批量上傳。
圖 7-12 文件批量寫入文件路徑輸入框
例如打開Word程序,新建一個空白文檔,然后依次點擊圖片>>此設備…,打開插入圖片對話框,并在【文件路徑輸入框】中輸入批量上傳的文件,最后點擊【插入】將多個圖片插入到word文檔中。代碼實現如下:
# upload_files.py
import time
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keysdef upload_files(driver, file_path, files: list):# 定位文件路徑輸入框file_input = WebDriverWait(driver, 60, 0.5).until(EC.visibility_of_element_located((By.XPATH, "//Edit[@Name='文件名(N):']")))# 輸入框輸入 Shift,由中文切換到英文file_input.send_keys(Keys.SHIFT)# 先輸入批量文件所在的目錄,進入該目錄下file_input.send_keys(file_path + Keys.ENTER)# 生成多個文件上傳時的字符串mult_file_str = ''for file in files:mult_file_str += " \"" + file + "\""# 文件上傳file_input.send_keys(mult_file_str)file_insert_btn = WebDriverWait(driver, 60, 0.5).until(EC.visibility_of_element_located((By.NAME, "插入(S)")))file_insert_btn.click()# 添加啟動參數
desired_caps = {}
desired_caps['app'] = r"C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE"
# 客戶端連接 Server,啟動 Session 會話
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', desired_capabilities=desired_caps)
driver.set_window_size(1000, 600)
time.sleep(1)
# 新建空白文檔
driver.find_element(by=By.NAME, value="空白文檔").click()
time.sleep(1)
# 依次點擊 插入 >> 圖片 >> 此設備...,打開文件上傳窗口
driver.find_element(by=By.NAME, value="插入").click()
driver.find_element(by=By.NAME, value="圖片").click()
driver.find_element(by=By.NAME, value="此設備...").click()# 文件上傳
upload_files(driver, r"D:", ['ty.png', 'ty2.png'])# 關閉程序和會話
driver.close()
driver.find_element(by=By.NAME, value="不保存").click()
driver.quit()
運行上面腳本,可以觀察到先啟動Word程序,然后點擊【空白文檔】新建了一個文檔,再然后依次點擊圖片>>此設備…打開了上傳文件窗口,上傳文件窗口中先是在【文件路徑輸入框】中輸入了"D:“進入到了D盤根目錄,接著就在【文件路徑輸入框】中輸入了"ty.png” “ty2.png”,并點擊了【插入】按鈕,此時可以看到Word文檔中已經成功插入了兩張圖片,如圖7-13所示,關閉寫字板程序,會話關閉。
圖 7-13 文件批量上傳
獲取通知欄信息
Windows系統屏幕右下角(任務欄最右側)的區域被稱為通知區域,通知區域最后一個圖標為通知欄圖標,點擊可打開通知欄,通知欄中可以查看所有被允許的通知信息。Windows也提供了快捷鍵迅速開啟通知欄,例如Win10系統上快捷鍵就是Win+a。
通知信息如Windows系統更新、截圖、安全通知、備份。例如關閉防火墻后通知欄會出現如圖7-14所示的消息。
圖 7-14 關閉防火墻通知
自動化腳本實現獲取通知欄信息的操作步驟是設置appTopLevelWindow為Root,定位桌面任意元素并發送快捷鍵打開通知欄,然后定位通知信息元素,獲取到通知元素后便可使用text接口得到文本內容。示例代碼如下:
# get_notification_message.py
import time
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keysdesired_caps = {}
desired_caps['app'] = "Root"
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', desired_capabilities=desired_caps)
time.sleep(1)# 通過快捷鍵打開 通知欄
driver.find_element(by=By.NAME, value="任務欄").send_keys(Keys.COMMAND + 'a' + Keys.COMMAND)
time.sleep(1)# 定位【來自 Windows 安全中心 的通知】通知元素
notification_element = driver.find_element(by=By.NAME, value="來自 Windows 安全中心 的通知")
# 獲取【來自 Windows 安全中心 的通知】下所有 Tag 為 Text 的元素文本,并且打印
text_elements = notification_element.find_elements(by=By.TAG_NAME, value="Text")
texts = [text_element.text for text_element in text_elements]
print(texts)# 通過快捷鍵關閉 通知欄
driver.find_element(by=By.NAME, value="任務欄").send_keys(Keys.COMMAND + 'a' + Keys.COMMAND)
# 關閉會話
driver.quit()
運行上面腳本后控制臺輸出內容如下:
['Windows 安全中心', '防火墻和網絡保護', '啟用 Windows 防火墻', 'Windows 防火墻已關閉。點擊或單擊以啟用。', '22:17']
運行上面腳本后,可以觀察到通知欄先開啟然后關閉。
Windows系統上,不止通知欄,許多操作都支持快捷鍵,例如打開文件資源管理器(Win+e)、打開反饋中心(Win+f)、打開剪貼板(Win+v),我們都可以通過此方法對其操作。
滾動條操作
滾動條是常見的一個控件,當內容超過顯示區域時便會出現滾動條,通過滑動滾動條可查看所有的內容。
WinAppDriver本身也實現了scroll(xoffset, yoffset)方法操作滾動條,使用方法為TouchActions(driver).scroll(0, 100).perform()。
除此之外,我們還可以接著鍵盤鍵PageUp和PageDown達到滑動滾動條的目的,下面通過發送鍵盤鍵滑動滾動條,選擇寫字板上的字體。打開字體選擇下拉列表后,在循環體中重復接下來的操作,首先查找預期的字體元素,如果出現則點擊字體元素,如果不出現則定位任意下拉列表框中的元素發送PageUp或PageDown鍵,如此循環,直到預期字體元素出現。代碼實現如下:
# scrollbar_operation.py
import time
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC# 添加啟動參數
desired_caps = {}
desired_caps['app'] = r"C:\Program Files\Windows NT\Accessories\wordpad.exe"
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', desired_capabilities=desired_caps)
driver.set_window_size(1000, 600)
time.sleep(1)# 字體選擇
combox_btn = WebDriverWait(driver, 30, 1).until(EC.visibility_of_element_located((By.XPATH, "//ComboBox[@Name='字體系列']/Button")))
combox_btn.click()i = 0
while i < 100:try:el = WebDriverWait(driver, 1, 1).until(EC.visibility_of_element_located((By.XPATH, "//ListItem[@Name='幼圓']")))el.click()breakexcept:i += 1temp_element = WebDriverWait(driver, 1, 1).until(EC.visibility_of_element_located((By.XPATH, "//List[@Name='字體系列']")))temp_element.send_keys(Keys.PAGE_DOWN)time.sleep(1)
# 關閉程序和會話
driver.close()
driver.quit()
運行上面腳本,可以觀察到打開字體選擇下拉列表后,相隔一定的時間滾動條就下滑一定的距離,當查找的幼圓字體后,點擊了幼圓字體,下來列表消失。
其他操作
運行在Windows系統上的應用程序,也有下拉框、單選、多選等控件,這些控件的操作與我們開發其它內容自動化腳本的思路是一致的。都是先熟悉手動操作步驟,然后封裝成測試方法,使用時調用封裝的方法即可。
例如寫字板中選擇字體下拉框。封裝思路為:先定位打開下拉按鈕并點擊,使下拉列表顯示出來,然后在下來列表中根據字體名查找字體元素,然后點擊該字體元素。代碼實現如下:
# combox_select.py
import time
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECdef combox_select(combox: str, value):combox_btn = WebDriverWait(driver, 30, 1).until(EC.visibility_of_element_located((By.XPATH, f"//ComboBox[@Name='{combox}']/Button")))combox_btn.click()value_item = WebDriverWait(driver, 30, 1).until(EC.visibility_of_element_located((By.XPATH, f"//ListItem[@Name='{value}']")))value_item.click()# 添加啟動參數
desired_caps = {}
desired_caps['app'] = r"C:\Program Files\Windows NT\Accessories\wordpad.exe"
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', desired_capabilities=desired_caps)
time.sleep(1)# 字體選擇
combox_select('字體系列', '黑體')time.sleep(1)
# 關閉程序和會話
driver.close()
driver.quit()
運行上面的腳本,會觀察到啟動寫字板后首先打開了字體選擇列表,然后點擊黑體字體選擇了字體,最后關閉寫字板,結束會話。
注意:選擇字體時要確保字體在下列列表的可視區域。
對于其他結構相同的下拉框,該方法也同樣適用,例如字體大小選擇20號字體,使用該方法時就可以寫成combox_select(‘字體大小’, 20)。
輔助工具Pywin32
PythonWin32模塊是一個非常受歡迎的模塊,它提供了從Python訪問許多Windows API的功能。自動化測試中使用它可以更方便地實現某些功能,例如窗口截圖。PythonWin32模塊作為一個成熟的模塊。可以輔助自動化測試項目更好地開展,提高工作效率。
Pywin32簡介
Pywin32模塊是一個第三方模塊庫,提供了很多訪問Windows系統的API,該項目是一個開源項目,在GitHub上可以看到項目源碼(https://github.com/mhammond/pywin32)。
Pywin32的安裝和Python的其它第三方庫安裝方式一樣,在命令行工具中輸入pip install pywin32即可完成安裝。完成后在Python安裝路徑下~\Lib\site-packages\win32可以看到所有API支撐模塊,如圖7-15。
圖 7-15 pywin32所有API支撐模塊
下面對部分模塊做以介紹:
win32api:封裝Windows Win32 API的模塊。
win32console:Windows控制臺函數的接口,用于處理字符模式應用程序。
win32event:提供win32事件/等待API接口的模塊。
win32file:win32文件API的接口,包括Vista引入的事務性NTFS操作。
win32gui:提供本機win32 GUI API的接口。
win32net:封裝Windows Network API的模塊。
win32process:提供win32進程和線程API的接口。
win32security:提供win32安全API的接口。
win32service:提供Windows NT Service API的接口。
win32ui:封裝Microsoft Foundation類的模塊。
其中win32api、win32gui和win32ui是較為重要的三個模塊,還有一個消息常量模塊win32con。通過這些API,我們可以獲取Windows窗口的相關信息,并做簡單的操作。
從pywin32所有API支撐模塊可以看出,Pywin32庫非常強大,提供了豐富的windows系統上的API接口。但是在WinApp自動化中,作為一個輔助工具,我們只用到他的邊角料功能就能滿足我們的需求。本節將會介紹自動化中使用Pywin32更好地幫助我們完成測試。
常用方法
win32 提供的API非常豐富,下面介紹一些常用的方法:
win32gui.FindWindow(ClassName, Title):獲取當前窗口句柄,句柄是窗口的唯一標識。參數ClassName是窗口的類名,Title是窗口標題。
win32gui. FindWindowEx(hld, Child, ClassName, Title):獲取父窗口下第一個窗口類為ClassName控件的窗口。參數hld是目標窗口的父窗口,Child是目標窗口的子窗口,ClassName是目標窗口的類名,Title是目標窗口的標題。
win32api.GetCursorPos():獲取當前窗口坐標,返回值是tuple類型,例如(721, 550)。
win32gui.GetWindowRect(handle):獲取窗口邊框矩形的左上角和右下角坐標,返回值是tuple類型,例如(-25600, -25600, -25441, -25573)。
win32gui.FindWindow(None, title):根據窗口名查找 Title窗口,返回值是窗口句柄。
win32gui.GetWindowText(handle):獲取窗口標題。
win32gui.GetClassName(handle):獲取窗口類名。
win32gui.BringWindowToTop(handle):將窗口放在最前面。
win32gui.SetForegroundWindow(handle):將窗口激活并放在最前面。
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0):鼠標左鍵按下。
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0):鼠標左鍵釋放。
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0):鼠標右鍵按下。
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0):鼠標右鍵釋放。
win32api.mouse_event(win32con.MOUSEEVENTF_WHEEL, 0, 0, -1):滑動界面,-1表示向下移動一個單位。
win32api.keybd_event(val, 0, 0, 0):按下鍵盤某個鍵。
win32api.keybd_event(val, 0, win32con.KEYEVENTF_KEYUP, 0):松開鍵盤的某個鍵。
獲取所有窗口句柄
獲取所有窗口句柄我們需要用到win32gui.EnumWindows(callBack,none) 方法,該方法是獲取windows所有窗口,無論窗口是否激活。當找到一個窗口就會執行callback一次,傳入 當前窗口句柄。將其封裝成方法,代碼如下:
def get_all_windows():handle_list = []win32gui.EnumWindows(lambda handle, param: param.append(handle), handle_list)return handle_list# 打印所有窗口 handle
print(get_all_windows())
# 打印窗口 title
print([win32gui.GetWindowText(handle) for handle in get_all_windows()])
鍵盤按鍵輸入
我們可以通過win32api.keybd_event(bVk, bScan, dwFlags, dwExtraInfo)方法實現鍵盤事件,因此可以利用它實現鍵盤按鍵輸入。
keybd_event(bVk, bScan, dwFlags, dwExtraInfo)方法有四個參數,bVk是虛擬鍵碼, bScan是硬件掃描碼,一般設置為0即可,dwFlags是函數操作的一個標志位,如果值為KEYEVENTF_EXTENDEDKEY則該鍵被按下,也可設置為0即可,如果值為KEYEVENTF_KEYUP則該按鍵被釋放,dwExtraInfo是定義與擊鍵相關的附加的32位值,一般設置為0即可。封裝方法如下:
def send_keyboard(*args):for arg in args:win32api.keybd_event(arg, 0, 0, 0)for arg in args:win32api.keybd_event(arg, 0, win32con.KEYEVENTF_KEYUP, 0)
例如 ctrl 鍵對應的鍵碼是 17,A鍵對應的鍵碼是65,那么使用封裝的方法發送ctrl+A組合鍵就可寫成send_keyboard([17, 65])。
鍵盤鍵碼請查看附件Ⅰ 鍵碼對照表。
窗口截圖
窗口截圖的思路是根據窗口句柄獲取窗口 DC 和窗口的位置信息及寬和高,然后創建一個新的 DC,再使用新創建的 DC 創建一個兼容設備內容的 DC,最后創建 bitmap,根據 DC 獲取圖像信息,保存成圖片文件。
DC在pywin32中是一個重要概念。windows不允許程序直接訪問硬件,所有的操作都需要通過一個設備上下文環境。屏幕上的每個窗口都對應一個DC。DC相當于一個視頻緩沖區,對這個緩沖區的操作,會表現在這個緩沖區對應的屏幕窗口上。除了窗口對應的DC外,還可以自己創建DC,然后在創建的DC上面建立數據拷貝到窗口的DC上,就相當于刷新窗口的DC。
示例:打開寫字板程序,并對寫字板當前窗口截圖。根據窗口截圖思路可實現截圖函數 screenshots_windows。因此實現截圖寫字板窗口的代碼如下:
# win32_screenshots.py
import timeimport win32gui, win32ui, win32con
from appium import webdriverdef screenshots_windows(windows_name):handle = win32gui.FindWindow(None, windows_name) # 獲取窗口句柄win32gui.SetForegroundWindow(handle) # 將窗口放在前臺,激活該窗口hdDC = win32gui.GetWindowDC(handle) # 獲取窗口 DCnewhdDC = win32ui.CreateDCFromHandle(hdDC) # 根據句柄創建一個DCsaveDC = newhdDC.CreateCompatibleDC() # 創建一個兼容設備內存的 DCsaveBitmap = win32ui.CreateBitmap() # 創建 bitmap 保存圖片# 獲取窗口的位置信息left, top, right, bottom = win32gui.GetWindowRect(handle)width = right – leftheight = bottom – top# bitmap 初始化saveBitmap.CreateCompatibleBitmap(newhdDC, width, height)saveDC.SelectObject(saveBitmap)saveDC.BitBlt((0, 0), (width, height), newhdDC, (0, 0), win32con.SRCCOPY)saveBitmap.SaveBitmapFile(saveDC, windows_name + ".png")desired_caps = {}
desired_caps['app'] = r"C:\Program Files\Windows NT\Accessories\wordpad.exe"
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', desired_capabilities=desired_caps)
time.sleep(3)
screenshots_windows("文檔 - 寫字板")
# 關閉會話
driver.quit()
注:此截屏函數screenshots_windows來自網絡。
運行上面代碼后,在當前目錄下多了一個“文檔 - 寫字板.png”文件,內容如圖7-16。
圖 7-16 pywin32截圖
腳本錄制
腳本錄制是通過工具記錄用戶的操作,并將操作生成腳本,已達到回放的目的。WinAppDriver社區也推出了一個開源的工具WinAppDriver UI Recorder,該工具可以讓用戶輕松地創建自動化的UI測試。下面我們就來學習WinAppDriver UI Recorder的使用。
1.下載WinAppDriver UI Recorder項目,下載地址:https://github.com/Microsoft/WinAppDriver/tree/master/Tools。例如筆者直接下載的是ZIP包,下載并解壓后會得到WinAppDriver-master一個文件夾。
2.安裝Visual Studio 2017及其以上版本的VS工具,例如筆者安裝的是VS 2022。
3.以管理員身份啟動VS,并且打開WinAppDriver UI Recorder項目。啟動VS后點擊【打開項目或解決方案§】,選擇~WinAppDriver-master\Tools\UIRecorder\WinAppDriverUIRecorder.sln文件,如圖7-17。
圖 7-17 VS打開WinAppDriverUIRecorder項目
4.編譯項目。在【解決方法資源管理器】下找到WinAppDriverUIRecorder,右鍵點擊后在彈窗的菜單中選擇【生成(U)】開始編譯項目,如圖7-18,如果沒有控制臺沒有報錯則編譯成功。
如果【解決方法資源管理器】沒有顯示,可在視圖菜單下點擊使其顯示。
圖 7-18 編譯WinAppDriverUIRecorder
5.點擊VS工具菜單中的【啟動】圖標:
運行項目,項目啟動成功后會打開一個【WAD UIRecorder】窗口,窗口結果如圖7-19(圖片來源WinAppDriver UI Recorder官方介紹文檔https://blogs.windows.com/windowsdeveloper/2018/06/20/introducing-winappdriver-ui-recorder/)。
7-19 WAD UIRecorder窗口
6.點擊【Record】激活錄制,然后在正文區域輸入一個“s”,如圖7-20。
7-20 錄制腳本
從WAD UIRecorder界面上面的面板中可以看到內容區域的xPath定位語法是:
"/Pane[@ClassName=\"#32769\"][@Name=\"桌面 1\"]/Window[@ClassName=\"WordPadClass\"][@Name=\"文檔 - 寫字板\"]/Document[@ClassName=\"RICHEDIT50W\"][@Name=\"多信息文本窗口\"]"
從下面的C# Code面板中可以看到操作生成的C#代碼,代碼如下:
// KeyboardInput VirtualKeys=""s"" CapsLock=False NumLock=True ScrollLock=False
Console.WriteLine("KeyboardInput VirtualKeys=\"\"s\"\" CapsLock=False NumLock=True ScrollLock=False");
System.Threading.Thread.Sleep(100);
winElem_LeftClickDocument多信息文本窗口_24_70.SendKeys("s");
使用WinAppDriver UI Recorder工具,用戶可以通過更簡單、更直觀的方法來為WinAppDriver編寫自動化腳本。
雖然生成的代碼是C#,但是通過該工具可以幫助我們迅速生成測試腳本,對于一些難以用元素識別工具獲得元素屬性的元素,也可以快速得到定位語法,這也是UI Recorder初始版本最先支持的兩個場景。
?
總結:
感謝每一個認真閱讀我文章的人!!!
作為一位過來人也是希望大家少走一些彎路,如果你不想再體驗一次學習時找不到資料,沒人解答問題,堅持幾天便放棄的感受的話,在這里我給大家分享一些自動化測試的學習資源,希望能給你前進的路上帶來幫助。
軟件測試面試文檔
我們學習必然是為了找到高薪的工作,下面這些面試題是來自阿里、騰訊、字節等一線互聯網大廠最新的面試資料,并且有字節大佬給出了權威的解答,刷完這一套面試資料相信大家都能找到滿意的工作。
?
? ? ? ? ? 視頻文檔獲取方式:
這份文檔和視頻資料,對于想從事【軟件測試】的朋友來說應該是最全面最完整的備戰倉庫,這個倉庫也陪伴我走過了最艱難的路程,希望也能幫助到你!以上均可以分享,點下方小卡片即可自行領取。