• WPF: 使用TestApi模拟用户输入


    参考资料:

    (1). WPF:自动点击某个FrameworkElement

    (2). TestApi - a library of Test APIs

    (3). Introduction to TestApi – Part 1: Input Injection APIs

    1. 模拟用户输入的五种方式:

     (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和C两种方式,其中B方式由AutomationUtilities类实现,C方式由Mouse 和 Keyboard两个类实现。

    2. 使用TestApi模拟的例子

        例1:在WPF Window中查找并按下一个WPF Button,使用AutomationUtilitiesMouse 类.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //
    // 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

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //
    // 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类可以在任何窗体应用程序中使用,与测试框架和测试流程无关,并且提供了源代码和文档,你可以集成到自己的project中,也可以直接引用Dll

      需要注意的是,虽然TestApi提供如此简单的方法实现UI测试,但是UI测试是一件棘手而复杂的事情,在任何时候都应该尽量避免。一般来说,宁可在应用程序中采用多层设计模式(multi-tier),而设计一个浅\薄(thin)的UI,以尽量规避UI测试。

    4. 附:如何计算控件位置

       如需单独计算控件元素位置,而不是使用TestApi中的GetClickablePoint()方法,可采用以下方法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /// <summary>
    2: /// Get mouse move to location
    
    3: /// </summary>
    
    4: /// <param name="element">element</param>
    
    5: /// <param name="logicalOffset">wpf logical pixel offset</param>
    
    6: /// <returns>screen physical pixel location</returns>
    
    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: /// <summary>
    
    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: /// </summary>
    
    38: /// <param name="logicalPixel">Logical(WPF) pixel value</param>
    
    39: /// <returns>Physical(Screen) pixel value</returns>
    
    40: public static int ConvertToPhysicalPixel(double logicalPixel)
    
    41: {
    
    42:    return Convert.ToInt32(logicalPixel * GetDpi() / 96.0);
    
    43: }
    
    44:  
    
    45: /// <summary>
    
    46: /// Get DPI of the system
    
    47: /// </summary>
    
    48: /// <returns></returns>
    
    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:   }
    
    
  • 相关阅读:
    级数问题
    放苹果
    _WIN32_WINNT not defined错误 解决办法
    日期大写
    金额大写转换
    选择屏幕字段不允许直接输入…
    OO面向对象ALV小测试
    判断是否有人在操作某张表,并获取…
    屏幕中设置焦点
    前导零
  • 原文地址:https://www.cnblogs.com/luluping/p/2057935.html
Copyright © 2020-2023  润新知