
























2010-05-20 20:30 lixiong 阅读(5641) 评论() 收藏 举报
根据我UIA自动化测试的经验, 总结了下面代码集. 在这个代码集中, 包含了:
1. 一个WPF的窗体程序
2. 一个WinForm的窗体, 这个窗体作为Model Dialog被WPF主程序打开
3. 针对这个WPF和WinForm的测试代码例子
4. 针对Win7 Calc.exe的测试代码例子
5. 一个简单的TestEngine
这个代码集的作用是:
1. 演示UIA中基本的概念, 比如AutomationID, AutomatonName, InvokePattern等的调用
2. 演示如何处理UI自动化的timing issue.
3. 演示简单WaitForReady的实现方法
4. 演示Click和Invoke的差别
5. 演示一个简单的UIA Engine
6. 演示如何通过AutomationPeer来给自绘画图案实现Invoke Pattern
7. 演示如何对WinForm实现Server side provider
8. 演示如何对WPF的databinding item设定AutomationID
WPF主窗口代码:
代码
public partial class Window1 : Window
{
private StackPanel pane;
private TextBlock timeBlock;
private Button buttonOpenNewWindow;
private ListBox listboxStringBinding;
private ListBox listboxManual;
private SelfDrawControl selfControl;
private DispatcherTimer tmr=new DispatcherTimer();
private DispatcherTimer deplyedExecution=new DispatcherTimer();
private Button kickoffFlashWindow;
private Button startWinformHoster;
private Button buttonNoAutomationID;
private Button busyButton;private int busyCount = 0;public Window1()
{
InitializeComponent();
this.Name = "Window1";
CreateControls();
}void CreateControls()
{
pane = new StackPanel();
pane.Name = "StackPane";
this.Content = pane;
timeBlock
= new TextBlock();buttonOpenNewWindow
= new Button();selfControl
= new SelfDrawControl();listboxManual
= new ListBox();kickoffFlashWindow
= new Button();startWinformHoster
= new Button();tmr.Interval
= new TimeSpan(0,0,0,0,500);buttonNoAutomationID
= new Button();busyButton
= new Button();deplyedExecution.Interval
= new TimeSpan(0, 0, 5);WPF自绘窗口的代码, 以及对应AutomationPeer的实现:
代码
public partial class SelfDrawControl : UserControl
{
public SelfDrawControl()
{
InitializeComponent();
}protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(Brushes.Blue, new Pen(Brushes.Blue, 10), new Rect(new Point(0, 0), new Point(RenderSize.Width / 2, RenderSize.Height)));
dc.DrawRectangle(Brushes.Black, new Pen(Brushes.Black, 10), new Rect(new Point(RenderSize.Width / 2, 0), new Point(RenderSize.Width, RenderSize.Height)));
}protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
Point point = e.GetPosition(this);
if (point.X > 0 && point.X < RenderSize.Width / 2)
{
DoClick("blue");
}
else
{
DoClick("black");
}
base.OnMouseLeftButtonDown(e);
}internal void DoClick(string color)
{
MessageBox.Show(color);
}protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
var peer = new SelfDrawControlAutomationPeer(this);
peer.InvalidatePeer();
return peer;
}
}public class SelfDrawControlAutomationPeer :UserControlAutomationPeer
{
SelfDrawControl target;
List<AutomationPeer> children = null;
public SelfDrawControlAutomationPeer(SelfDrawControl target):base(target)
{
this.target = target;
}protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}protected override string GetClassNameCore()
{
return target.GetType().ToString();
}/*
* 特别注意, 生成ChildrenCore的时候务必维护父子关系
* UI Testclient 可能在不同child parent之间遍历
* 务必保证childpeer.Parent = parent.Children[n]
* 否则会带来各种意外,这是在实现自定义Peer时候的最大陷阱
*/ protected override List<AutomationPeer> GetChildrenCore()
{
//return null;
if (children == null)
{
children = new List<AutomationPeer>();
SelfDrawControlElementAutomationPeer bluepeer = new SelfDrawControlElementAutomationPeer(target, this, "blue");
SelfDrawControlElementAutomationPeer redpeer = new SelfDrawControlElementAutomationPeer(target, this, "red");
children.Add(bluepeer);
children.Add(redpeer);
}return children;
}
}
public class SelfDrawControlElementAutomationPeer : AutomationPeer, IInvokeProvider, IValueProvidervar o
=this.GetParent();Rect parentRect
=parentPeer.GetBoundingRectangle();if(color=="blue")BindingWindow的XAML和代码:
<ListBox Name="InnerListbox" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}" ></Setter>
</Style>
</ListBox.Resources>
</ListBox>
代码
public partial class BindingWithAutomationID : Window
{
public BindingWithAutomationID()
{
InitializeComponent();
this.Name = "StyleBindingWindow";//可以使用Style的Setter来给binding的子元素增加AutomationID
this.InnerListbox.DataContext = new string[] { "1", "2", "3" };
Binding bind = new Binding();
bind.Source = new string[] { "1", "2", "3" };
this.InnerListbox.SetBinding(ListBox.ItemsSourceProperty, bind);
}
}
WinForm的代码及其Server side provider实现:
代码
[ComVisible(true)]
public class MyForm : System.Windows.Forms.Form, IRawElementProviderSimple
{
private ValuePattern vp = new ValuePattern();
private Timer timer = new Timer();
private string curentNameProperty = string.Empty;public MyForm()
{
timer.Interval = 500;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;this.Name = "WinFormWindow";
this.Text = "ServerUIAFormDemo";
Button btn
= new Button();curentNameProperty
= string.Format("{0} {1}", this.Name, DateTime.Now.ToLongTimeString());m.Result
= AutomationInteropProvider.ReturnRawElementProvider(}
base.WndProc(ref m);}
public IRawElementProviderSimple HostRawElementProvider[ComVisible(
true)]下面是测试程序:
测试演示1: 演示UIA API里面的cached property:
代码
class DemoCachedProperty
{
public static void Test()
{
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement textBlock1 = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
AutomationElement textBlock2 = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
for (int i = 0; i < 100; i++)
{
string cachedName = textBlock1.Cached.Name;
string uncachedName = textBlock2.Current.Name;
Console.WriteLine("=================");
Console.WriteLine("Cached Name is {0}", cachedName);
Console.WriteLine("UnCached Name is {0}", uncachedName);
System.Threading.Thread.Sleep(300);
}
}
Console.WriteLine(
"Test finishes...................");测试演示2: timing issue导致的问题和三种应对方法: Sleep/Polling/Event:
代码
class DemoDelayedWindow
{
static DateTime eventStartTime;public static void NoUISyncLeadToErrorDemo()
{
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
invokPtn.Invoke();
Console.WriteLine("Button Clicked...");
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
Console.WriteLine(newWindow.Current.Name);
}
Console.WriteLine(
"Test finishes...................");CacheRequest cacheRequest
= new CacheRequest();Console.WriteLine(
"Test finishes...................");}
Console.WriteLine(
"Test finishes..................."); invokPtn.Invoke();
Console.WriteLine(
AutomationElement sourceElement;
tryConsole.WriteLine(
"Test finishes...................");测试演示3: 通过Waiter Pattern来简化timing issue的处理:
代码
class SimpleWaiter
{
private AutomationEvent _eventId;
private AutomationElement _element;
private TreeScope _scope;
private System.Threading.AutoResetEvent _event;
private Condition _condition;
private AutomationEventHandler _eventHandler;public SimpleWaiter(AutomationEvent eventId, AutomationElement element, TreeScope scope, Condition condition)
{
this._eventId = eventId;
this._element = element;
this._scope = scope;
this._condition = condition;
_event = new System.Threading.AutoResetEvent(false);
_eventHandler = new AutomationEventHandler(Handler);
Automation.AddAutomationEventHandler(_eventId, _element, _scope, _eventHandler);
}void Handler(object src, AutomationEventArgs e)
{
AutomationElement sourceElement;
sourceElement = src as AutomationElement;
var finditem = sourceElement.FindFirst(TreeScope.Element, _condition);
if (finditem != null && finditem.Equals(sourceElement))
{
_event.Set();
Automation.RemoveAutomationEventHandler(_eventId, _element, _eventHandler);
}
}public void Wait(int timeOut)
{
_event.WaitOne(timeOut);
} public static void WaiterDemo()
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
SimpleWaiter waiter
= new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow")); invokPtn.Invoke();
Console.WriteLine(
AutomationElement newWindow
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));Console.WriteLine(
"Test finishes...................");测试演示3: 如何模拟真实的鼠标click, 以及如何确保测试目标位于前台:
代码
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint msg;
public ushort paramL;
public ushort paramH;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT[StructLayout(LayoutKind.Explicit)]
public struct InputTypeUnion[StructLayout(LayoutKind.Sequential)]
public struct INPUT
代码
public static void ClickWithWaiterDemo()
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
WindowPattern wndptn = (WindowPattern)wpfRoot.GetCurrentPattern(WindowPattern.Pattern);
var currentStates
= wndptn.Current.WindowVisualState;SimpleWaiter waiter
= new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));AutomationElement newWindow
= AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));Console.WriteLine(
"Test finishes...................");测试演示4,5演示WaitForReady以及简单的Engine实现, 代码比较多, 就请自己下载吧.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。