WPF:TestApiを用いてユーザ入力をシミュレートする
11680 ワード
参考資料:
(1). WPF:FrameworkElementを自動的にクリック
(2). TestApi - a library of Test APIs
(3). Introduction to TestApi – Part 1: Input Injection APIs
1.ユーザー入力をシミュレートする5つの方法:
(A)UI elementを直接呼び出す方法、例えば:Button.IsPressed
(B)利用可能なインターフェース(UIA,MSAA,etc.)、例えば:AutomationElement
(C)下部入力シミュレーションを使用して、オペレーティングシステムに関連して、例えば:WindowsのSendInput Win32 APIとRaw Input Win32 API
(D)デバイス駆動シミュレーションの使用
(E)ロボットを用いて人間の操作をシミュレートし、例えばキーボードを叩く
方法Aはframeworkレベルであり、WPFに対してのみ有効であり、Winformに対しては無効である.
方法Bはframeworkよりもレベルが低いが、いくつかのframeworkが必要とする利用可能なポート実装方式が異なるため、多くの制限がある.
方法CとDはオペレーティングシステムレベルであり、DはCより実現しにくい.
方法Eは一般的に使われている方法です(アメリカだけだと思いますが、汗)、代価が高くてスピードが遅いです.
TestApiは、B方式がAutomationUtilitiesクラス、C方式がMouseとKeyboardの2つのクラスで実装される最も一般的なBとCの2つの方式を提供しています.
2.TestApiシミュレーションを用いた例
例1:WPF WindowでWPF Buttonを検索して押す、AutomationUtilitiesとMouseクラスを使用する.
例2:TextBoxを自動的に検索し、その中でタイプし、MouseとKeyboardクラスを使用する
3.後記
TestApiのMouseクラスとKeyboardクラスは、任意のフォームアプリケーションで使用できます.テストフレームワークやテストプロセスに関係なく、ソースコードとドキュメントが提供されています.自分のプロジェクトに統合したり、Dllを直接参照したりすることができます.
なお、TestApiはこのような簡単な方法でUIテストを実現しているが、UIテストは厄介で複雑なことであり、いつでも避けるべきである.一般的に、UIテストを最小限に抑えるために、アプリケーションで多層設計モード(multi-tier)を採用し、UIテストを最小限に抑えるために浅いUIを設計します.
4.添付:コントロールの位置を計算する方法
TestApiのGetClickablePoint()メソッドではなく、コントロール要素の位置を個別に計算するには、次の方法を使用します.
1:///
2:///Get mouse move to location
3:///
4:///element
5:///wpf logical pixel offset
6:///screen physical pixel location
7: public static System.Drawing.Point GetMoveToLocation(FrameworkElement element, Point logicalOffset)
8: {
9: Point mouseLocation = default(Point);
10: FlowDirection flowDirection = Window.GetWindow(element).FlowDirection;
11:
12: //We don't need to convert element location to physical screen pixel because wpf takes care of it.
13: Point elementLocation = element.PointToScreen(new Point());
14:
15://We need to convert offset to physical screen pixel since we're pass in wpf logical pixel
16: double physicalXOffset = ConvertToPhysicalPixel(logicalOffset.X);
17: double physicalYOffset = ConvertToPhysicalPixel(logicalOffset.Y);
18:
19: switch (flowDirection)
20: {
21: case FlowDirection.LeftToRight:
22: mouseLocation = new Point(elementLocation.X + physicalXOffset, elementLocation.Y + physicalYOffset);
23: break;
24: case FlowDirection.RightToLeft:
25: //We need to subtract physical offsetX because the element location starting point is in right most
26: mouseLocation = new Point(elementLocation.X - physicalXOffset, elementLocation.Y + physicalYOffset);
27: break;
28: }
29:
30: return new System.Drawing.Point(Convert.ToInt32(mouseLocation.X), Convert.ToInt32(mouseLocation.Y));
31: }
32:
33:///
34:///WPF has its own pixel system in double value type, and screen pixel includes different DPIs is in int value type.
35:///In 96 dpi, wpf and screen pixels are the same, but other dpi, we need to convert wpf logical pixel to screen physical
36:///pixel by using formula (wpf pixel value * dpi/96.0).
37:///
38:///Logical(WPF) pixel value
39:///Physical(Screen) pixel value
40: public static int ConvertToPhysicalPixel(double logicalPixel)
41: {
42: return Convert.ToInt32(logicalPixel * GetDpi()/96.0);
43: }
44:
45:///
46:///Get DPI of the system
47:///
48:///
49: public static float GetDpi()
50: {
51: using (System.Drawing.Graphics graph = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
52: {
53: if (graph == null)
54: {
55: throw new NullReferenceException("Graphics not found");
56: }
57:
58: if (!graph.DpiX.Equals(graph.DpiY))
59: {
60: throw new ArithmeticException("DpiX != DpiY");
61: }
62:
63: return graph.DpiX;
64: }
65: }
(1). WPF:FrameworkElementを自動的にクリック
(2). TestApi - a library of Test APIs
(3). Introduction to TestApi – Part 1: Input Injection APIs
1.ユーザー入力をシミュレートする5つの方法:
(A)UI elementを直接呼び出す方法、例えば:Button.IsPressed
(B)利用可能なインターフェース(UIA,MSAA,etc.)、例えば:AutomationElement
(C)下部入力シミュレーションを使用して、オペレーティングシステムに関連して、例えば:WindowsのSendInput Win32 APIとRaw Input Win32 API
(D)デバイス駆動シミュレーションの使用
(E)ロボットを用いて人間の操作をシミュレートし、例えばキーボードを叩く
方法Aはframeworkレベルであり、WPFに対してのみ有効であり、Winformに対しては無効である.
方法Bはframeworkよりもレベルが低いが、いくつかのframeworkが必要とする利用可能なポート実装方式が異なるため、多くの制限がある.
方法CとDはオペレーティングシステムレベルであり、DはCより実現しにくい.
方法Eは一般的に使われている方法です(アメリカだけだと思いますが、汗)、代価が高くてスピードが遅いです.
TestApiは、B方式がAutomationUtilitiesクラス、C方式がMouseとKeyboardの2つのクラスで実装される最も一般的なBとCの2つの方式を提供しています.
2.TestApiシミュレーションを用いた例
例1:WPF WindowでWPF Buttonを検索して押す、AutomationUtilitiesとMouseクラスを使用する.
//
//
EXAMPLE #1
//
This code below discovers and clicks the Close button in an About
//
dialog box, thus dismissing the About dialog box.
//
string
aboutDialogName
=
"
About
"
;
string
closeButtonName
=
"
Close
"
;
AutomationElementCollection aboutDialogs
=
AutomationUtilities.FindElementsByName(
AutomationElement.RootElement,
aboutDialogName);
AutomationElementCollection closeButtons
=
AutomationUtilities.FindElementsByName(
aboutDialogs[
0
],
closeButtonName);
//
//
You can either invoke the discovered control, through its invoke
//
pattern...
//
InvokePattern p
=
closeButtons[
0
].GetCurrentPattern(InvokePattern.Pattern)
as
InvokePattern;
p.Invoke();
//
//
... or you can handle the Mouse directly and click on the control.
//
Mouse.MoveTo(closeButton.GetClickablePoint());
Mouse.Click(MouseButton.Left);
例2:TextBoxを自動的に検索し、その中でタイプし、MouseとKeyboardクラスを使用する
//
//
EXAMPLE #2
//
Discover the location of a TextBox with a given name.
//
string
textboxName
=
"
ssnInputField
"
;
AutomationElement textBox
=
AutomationUtilities.FindElementsByName(
AutomationElement.RootElement,
textboxName)[
0
];
Point textboxLocation
=
textbox.GetClickablePoint();
//
//
Move the mouse to the textbox, click, then type something
//
Mouse.MoveTo(textboxLocation);
Mouse.Click(MouseButton.Left);
Keyboard.Type(
"
Hello world.
"
);
Keyboard.Press(Key.Shift);
Keyboard.Type(
"
hello, capitalized world.
"
);
Keyboard.Release(Key.Shift);
3.後記
TestApiのMouseクラスとKeyboardクラスは、任意のフォームアプリケーションで使用できます.テストフレームワークやテストプロセスに関係なく、ソースコードとドキュメントが提供されています.自分のプロジェクトに統合したり、Dllを直接参照したりすることができます.
なお、TestApiはこのような簡単な方法でUIテストを実現しているが、UIテストは厄介で複雑なことであり、いつでも避けるべきである.一般的に、UIテストを最小限に抑えるために、アプリケーションで多層設計モード(multi-tier)を採用し、UIテストを最小限に抑えるために浅いUIを設計します.
4.添付:コントロールの位置を計算する方法
TestApiのGetClickablePoint()メソッドではなく、コントロール要素の位置を個別に計算するには、次の方法を使用します.
1:///
2:///Get mouse move to location
3:///
4:///element
5:///wpf logical pixel offset
6:///
7: public static System.Drawing.Point GetMoveToLocation(FrameworkElement element, Point logicalOffset)
8: {
9: Point mouseLocation = default(Point);
10: FlowDirection flowDirection = Window.GetWindow(element).FlowDirection;
11:
12: //We don't need to convert element location to physical screen pixel because wpf takes care of it.
13: Point elementLocation = element.PointToScreen(new Point());
14:
15://We need to convert offset to physical screen pixel since we're pass in wpf logical pixel
16: double physicalXOffset = ConvertToPhysicalPixel(logicalOffset.X);
17: double physicalYOffset = ConvertToPhysicalPixel(logicalOffset.Y);
18:
19: switch (flowDirection)
20: {
21: case FlowDirection.LeftToRight:
22: mouseLocation = new Point(elementLocation.X + physicalXOffset, elementLocation.Y + physicalYOffset);
23: break;
24: case FlowDirection.RightToLeft:
25: //We need to subtract physical offsetX because the element location starting point is in right most
26: mouseLocation = new Point(elementLocation.X - physicalXOffset, elementLocation.Y + physicalYOffset);
27: break;
28: }
29:
30: return new System.Drawing.Point(Convert.ToInt32(mouseLocation.X), Convert.ToInt32(mouseLocation.Y));
31: }
32:
33:///
34:///WPF has its own pixel system in double value type, and screen pixel includes different DPIs is in int value type.
35:///In 96 dpi, wpf and screen pixels are the same, but other dpi, we need to convert wpf logical pixel to screen physical
36:///pixel by using formula (wpf pixel value * dpi/96.0).
37:///
38:///Logical(WPF) pixel value
39:///
40: public static int ConvertToPhysicalPixel(double logicalPixel)
41: {
42: return Convert.ToInt32(logicalPixel * GetDpi()/96.0);
43: }
44:
45:///
46:///Get DPI of the system
47:///
48:///
49: public static float GetDpi()
50: {
51: using (System.Drawing.Graphics graph = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
52: {
53: if (graph == null)
54: {
55: throw new NullReferenceException("Graphics not found");
56: }
57:
58: if (!graph.DpiX.Equals(graph.DpiY))
59: {
60: throw new ArithmeticException("DpiX != DpiY");
61: }
62:
63: return graph.DpiX;
64: }
65: }