模拟滑鼠在自動化測試中的應用
盡管UI Automation可以實作控件的自動化,但是,自定義控件的自動化往往由于自定義控件中對自動化的支援不夠,而導緻無法使用UI Automation的相關Pattern來操作此控件。此時我們可以使用模拟滑鼠的操作來達到相應的效果。
. NET并沒有提供改變滑鼠指針位置、模拟點選操作的函數;但是Windows API提供了。在.NET中模拟滑鼠通常是調用底層的API來實作。
注:在使用前必須添加using System.Runtime.InteropServices;命名空間。 |
移動滑鼠到指定的位置
/// <summary> /// Add mouse move event /// </summary> /// <param name="x">Move to specify x coordinate</param> /// <param name="y">Move to specify y coordinate</param> /// <returns></returns> [DllImport("user32.dll")] extern static bool SetCursorPos(int x, int y); |
該函數可以改變滑鼠指針的位置。其中x,y是相對于螢幕左上角的絕對位置。
移動滑鼠到指定的位置并進行相應的滑鼠操作
/// Mouse click event /// <param name="mouseEventFlag">MouseEventFlag </param> /// <param name="incrementX">X coordinate</param> /// <param name="incrementY">Y coordinate</param> /// <param name="data"></param> /// <param name="extraInfo"></param> [DllImport("user32.dll")] extern static void mouse_event(int mouseEventFlag, int incrementX, int incrementY, uint UIntPtr ); |
這個函數不僅可以設定滑鼠指針絕對的位置,而且可以以相對坐标來設定。另外,該函數還可以模拟滑鼠左右鍵點選、滑鼠滾輪操作等。其中的MouseEventFlag是一個常量,定義如下:
const int MOUSEEVENTF_MOVE = 0x0001; const int MOUSEEVENTF_LEFTDOWN = 0x0002; const int MOUSEEVENTF_LEFTUP = 0x0004; const int MOUSEEVENTF_RIGHTDOWN = 0x0008; const int MOUSEEVENTF_RIGHTUP = 0x0010; const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; const int MOUSEEVENTF_MIDDLEUP = 0x0040; const int MOUSEEVENTF_ABSOLUTE = 0x8000; |
上面列舉的兩個函數的詳細說明,可參考MSDN幫助文檔。
如下代碼為在控件的指定位置上單擊滑鼠左鍵和右鍵。
public void ClickLeftMouse(int processId, string automationId) { AutomationElement element = FindElementById(processId, automationId); if (element == null) { throw new NullReferenceException(string.Format("Element with AutomationId '{0}' and Name '{1}' can not be find.", element.Current.AutomationId, element.Current.Name)); } Rect rect = element.Current.BoundingRectangle; int IncrementX = (int)(rect.Left + rect.Width / 2); int IncrementY = (int)(rect.Top + rect.Height / 2); //Make the cursor position to the element. SetCursorPos(IncrementX, IncrementY); //Make the left mouse down and up. mouse_event(MOUSEEVENTF_LEFTDOWN, IncrementX, IncrementY, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, IncrementX, IncrementY, 0, 0); } public void ClickRightMouse(int processId, string automationId) mouse_event(MOUSEEVENTF_RIGHTDOWN, IncrementX, IncrementY, 0, 0); mouse_event(MOUSEEVENTF_RIGHTUP, IncrementX, IncrementY, 0, 0); |
在上面的代碼中,我們通過如下步驟來達到點選滑鼠的目的:
1. 通過UI Automation來找到需要滑鼠點選的控件;
2. 然後取得此控件的坐标;
3. 在設定點選坐标在控件的中心位置;
4. 移動滑鼠的位置到控件的中心位置(此步驟可以不要,但是為了達到雙保險的效果,我們還是移動一次)。
5. 調用Win32 API模拟滑鼠點選控件。
注:Rect需要引用WindowBase.dll,添加using System.Windows;明明空間。 |
下面的例子示範了通過模拟滑鼠左鍵點選button來彈出對話框,并且模拟滑鼠點選對話框中的“OK”按鈕關閉對話框。
using System; using System.Text; using System.Diagnostics; using System.Threading; using System.Windows.Automation; using System.Runtime.InteropServices; using System.Windows; namespace UIATest class Program static void Main(string[] args) { Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\ATP.TestForm\bin\Debug\ATP.TestForm.exe"); int processId = process.Id; Thread.Sleep(1000); ClickLeftMouse(processId, "button1"); Thread.Sleep(3000); ClickLeftMouse(processId, "2"); Console.WriteLine("Test finised."); } #region ClickMouse #region Import DLL /// <summary> /// Add mouse move event /// </summary> /// <param name="x">Move to specify x coordinate</param> /// <param name="y">Move to specify y coordinate</param> /// <returns></returns> [DllImport("user32.dll")] extern static bool SetCursorPos(int x, int y); /// Mouse click event /// <param name="mouseEventFlag">MouseEventFlag </param> /// <param name="incrementX">X coordinate</param> /// <param name="incrementY">Y coordinate</param> /// <param name="data"></param> /// <param name="extraInfo"></param> extern static void mouse_event(int mouseEventFlag, int incrementX, int incrementY, uint data, UIntPtr extraInfo); const int MOUSEEVENTF_MOVE = 0x0001; const int MOUSEEVENTF_LEFTDOWN = 0x0002; const int MOUSEEVENTF_LEFTUP = 0x0004; const int MOUSEEVENTF_RIGHTDOWN = 0x0008; const int MOUSEEVENTF_RIGHTUP = 0x0010; const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; const int MOUSEEVENTF_MIDDLEUP = 0x0040; const int MOUSEEVENTF_ABSOLUTE = 0x8000; #endregion public static void ClickLeftMouse(int processId, string automationId) AutomationElement element = FindElementById(processId, automationId); if (element == null) { throw new NullReferenceException(string.Format("Element with AutomationId '{0}' and Name '{1}' can not be find.", element.Current.AutomationId, element.Current.Name)); } Rect rect = element.Current.BoundingRectangle; int IncrementX = (int)(rect.Left + rect.Width / 2); int IncrementY = (int)(rect.Top + rect.Height / 2); //Make the cursor position to the element. SetCursorPos(IncrementX, IncrementY); //Make the left mouse down and up. mouse_event(MOUSEEVENTF_LEFTDOWN, IncrementX, IncrementY, 0, UIntPtr.Zero); mouse_event(MOUSEEVENTF_LEFTUP, IncrementX, IncrementY, 0, UIntPtr.Zero); #endregion /// Get the automation elemention of current form. /// <param name="processId">Process Id</param> /// <returns>Target element</returns> public static AutomationElement FindWindowByProcessId(int processId) AutomationElement targetWindow = null; int count = 0; try Process p = Process.GetProcessById(processId); targetWindow = AutomationElement.FromHandle(p.MainWindowHandle); return targetWindow; catch (Exception ex) count++; StringBuilder sb = new StringBuilder(); string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString(); if (count > 5) { throw new InvalidProgramException(message, ex); } else return FindWindowByProcessId(processId); /// Get the automation element by automation Id. /// <param name="windowName">Window name</param> /// <param name="automationId">Control automation Id</param> /// <returns>Automatin element searched by automation Id</returns> public static AutomationElement FindElementById(int processId, string automationId) AutomationElement aeForm = FindWindowByProcessId(processId); AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, automationId)); return tarFindElement; |