桌面應用自動化winappdriver
關於winappdriver
介紹
- WinAppDriver全稱是Windows Application Driver,它提供了一些API,使得使用者可以像selenium操作web一樣來操作windows的應用程式
- 它支援的系統是Windows 10 (Home and Pro) 和Windows Server 2016
- 原始碼暫未開源
- WinAppDriver可以獨立執行,也可以作為appium的一個外掛來使用
支援應用型別
-
UWP – Universal Windows Platform, also known as Universal Apps or Modern Apps, It's Microsoft’s latest desktop application technology. It's XAML based. Only runs on Windows 10 machines
-
WPF - also XAML based, much more mature, runs on any Windows version and has been around since 2006.
-
WinForms - one of the older technologies, now found mostly on legacy applications.
WPF和WinForms 是兩套介面渲染方式。一個是對傳統windows介面元素的封裝,透過gdi繪製。另一個是全新的dx渲染繪製的介面,也脫離了對傳統windows控制元件的依賴,沒有歷史包袱,理論上可以展現更炫酷的介面。
-
MFC/Classic Windows - MFC is a UI library normally paired with Win32 applications. This option is normally chosen when more efficiency is needed with low-level C++ handling or when supporting non-Microsoft platforms.
資源
素材 | 地址 | 說明 |
---|---|---|
FlaUInspect | https://github.com/FlaUI/FlaUInspect/releases | 定位工具 |
WinAppDriver | https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1 | |
UIRecorder | https://github.com/microsoft/WinAppDriver/tree/master/Tools/UIRecorder | 定位工具 |
inspect | 微軟官方工具整合於 Windows SDK | 定位工具 |
- UIRecorder(下文不涉及,僅供參考與備忘)
- Open
WinAppDriverUIRecorder.sln
in Visual Studio- Select Debug > Start Debugging or simply Run
支援的定位方式
Client API | Locator Strategy | Matched Attribute in inspect.exe |
Example |
---|---|---|---|
FindElementByAccessibilityId | accessibility id | AutomationId | AppNameTitle |
FindElementByClassName | class name | ClassName | TextBlock |
FindElementById | id | RuntimeId (decimal) | 42.333896.3.1 |
FindElementByName | name | Name | Calculator |
FindElementByTagName | tag name | LocalizedControlType (upper camel case) | Text |
FindElementByXPath | xpath | Any | //Button[0] |
配置
開啟windows的開發者模式
- 你沒看錯,不是手機,windows也有
- 第一步:搜開發者設定
- 第二步:開啟開發人員模式
- 第三步:確認啟用
啟動winappdriver
-
不開啟開發人員模式的提示
C:\Program Files (x86)\Windows Application Driver>WinAppDriver.exe Developer mode is not enabled. Enable it through Settings and restart Windows Application Driver Failed to initialize: 0x80004005
-
開啟後啟動winappdriver
C:\Program Files (x86)\Windows Application Driver>WinAppDriver.exe Windows Application Driver listening for requests at: http://127.0.0.1:4723/ Press ENTER to exit.
-
還可以這樣啟動
WinAppDriver.exe 4727 WinAppDriver.exe 10.0.0.10 4725 WinAppDriver.exe 10.0.0.10 4723/wd/hub # 推薦
例項
appium-python-client 版本不要用2.0+,此處是1.2.0
記事本
-
比如記事本
from appium import webdriver des_cap = {} des_cap['app'] = r'C:\Windows\System32\notepad.exe' driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities=des_cap) driver.implicitly_wait(5) driver.find_element_by_name('檔案(F)').click() from time import sleep sleep(2) driver.find_element_by_name('儲存(S) Ctrl+S').click() # driver.find_element_by_name('退出(X)').click() sleep(1) import pyautogui pyautogui.PAUSE = 0.5 pyautogui.typewrite(r'D:\hello.txt') pyautogui.press('enter')
-
這裡的難點是
儲存(S) Ctrl+S
的獲取 -
這裡需要用到inspect.exe
計算器
- 你可能會寫這樣的程式碼
from appium import webdriver
des_cap = {}
des_cap['app'] = r'C:\Windows\System32\calc.exe'
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
desired_capabilities=des_cap)
driver.implicitly_wait(5)
- 但會報錯
Traceback (most recent call last):
File "D:/demo_calc.py", line 5, in <module>
desired_capabilities=des_cap)
File "D:\Python37\lib\site-packages\appium\webdriver\webdriver.py", line 157, in __init__
AppiumConnection(command_executor, keep_alive=keep_alive), desired_capabilities, browser_profile, proxy
File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 157, in __init__
self.start_session(capabilities, browser_profile)
File "D:\Python37\lib\site-packages\appium\webdriver\webdriver.py", line 226, in start_session
response = self.execute(RemoteCommand.NEW_SESSION, parameters)
File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Failed to locate opened application window with appId: C:\Windows\System32\calc.exe, and processId: 4472
程式已結束,退出程式碼為 1
- 開啟計算器,然後在powershell中執行如下命令
Get-StartApps |Select-String "計算器"
# 輸出
@{Name=計算器; AppID=Microsoft.WindowsCalculator_8wekyb3d8bbwe!App} # 你要的是這裡的AppID
- 程式碼
from appium import webdriver
des_cap = {}
des_cap['app'] = r'Microsoft.WindowsCalculator_8wekyb3d8bbwe!App'
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
desired_capabilities=des_cap)
driver.implicitly_wait(5)
driver.find_element_by_name('一').click()
driver.find_element_by_name('二').click()
driver.find_element_by_name('加').click()
driver.find_element_by_name('三').click()
driver.find_element_by_name('四').click()
driver.find_element_by_name('等於').click()
# 透過inspect 獲取 automationID
print(driver.find_element_by_accessibility_id('CalculatorResults').text) # 得到的是 ·顯示為 46· 你仍然要處理才能做測試
driver.quit()
計算器測試(官網)
我沒跑,僅供參考,你可以認為是為了增加篇幅
# https://raw.githubusercontent.com/microsoft/WinAppDriver/master/Samples/Python/calculatortest.py
import unittest
from appium import webdriver
class SimpleCalculatorTests(unittest.TestCase):
@classmethod
def setUpClass(self):
#set up appium
desired_caps = {}
desired_caps["app"] = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"
self.driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
desired_capabilities= desired_caps)
@classmethod
def tearDownClass(self):
self.driver.quit()
def getresults(self):
displaytext = self.driver.find_element_by_accessibility_id("CalculatorResults").text
displaytext = displaytext.strip("Display is " )
displaytext = displaytext.rstrip(' ')
displaytext = displaytext.lstrip(' ')
return displaytext
def test_initialize(self):
self.driver.find_element_by_name("Clear").click()
self.driver.find_element_by_name("Seven").click()
self.assertEqual(self.getresults(),"7")
self.driver.find_element_by_name("Clear").click()
def test_addition(self):
self.driver.find_element_by_name("One").click()
self.driver.find_element_by_name("Plus").click()
self.driver.find_element_by_name("Seven").click()
self.driver.find_element_by_name("Equals").click()
self.assertEqual(self.getresults(),"8")
def test_combination(self):
self.driver.find_element_by_name("Seven").click()
self.driver.find_element_by_name("Multiply by").click()
self.driver.find_element_by_name("Nine").click()
self.driver.find_element_by_name("Plus").click()
self.driver.find_element_by_name("One").click()
self.driver.find_element_by_name("Equals").click()
self.driver.find_element_by_name("Divide by").click()
self.driver.find_element_by_name("Eight").click()
self.driver.find_element_by_name("Equals").click()
self.assertEqual(self.getresults(),"8")
def test_division(self):
self.driver.find_element_by_name("Eight").click()
self.driver.find_element_by_name("Eight").click()
self.driver.find_element_by_name("Divide by").click()
self.driver.find_element_by_name("One").click()
self.driver.find_element_by_name("One").click()
self.driver.find_element_by_name("Equals").click()
self.assertEqual(self.getresults(),"8")
def test_multiplication(self):
self.driver.find_element_by_name("Nine").click()
self.driver.find_element_by_name("Multiply by").click()
self.driver.find_element_by_name("Nine").click()
self.driver.find_element_by_name("Equals").click()
self.assertEqual(self.getresults(),"81")
def test_subtraction(self):
self.driver.find_element_by_name("Nine").click()
self.driver.find_element_by_name("Minus").click()
self.driver.find_element_by_name("One").click()
self.driver.find_element_by_name("Equals").click()
self.assertEqual(self.getresults(),"8")
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(SimpleCalculatorTests)
unittest.TextTestRunner(verbosity=2).run(suite)