Neues rund um's Thema .Net, Team Foundation Server und SCRUM RSS 2.0
# Friday, July 03, 2009

Wie in diesem Blog bereits an anderen Stellen erläutert, eignet sich das UIA (UI Automation Framework) sehr gut, um UI-Tests aufzubauen. Wer sich mit dieser Möglichkeit beschäftigt wird aber früher oder später auf das Problem stoßen, dass UIA Support bei WinForms Controls nicht flächendeckend gegeben ist, vor allem bei 3rd Party Controls sieht es da oft eher mau aus.

Ich habe hier beschrieben, wie man mit einem ServerSide Provider diese Lücken selbst schließen kann. Das Standardvorgehen sieht dabei vor, dass man ein eigenes Control erstellt, das man dann von dem Ausgangscontrol ableitet. Diese Vorgehen ist in der Praxis allerdings nicht unproblematisch. Zum einen muss man die abgeleiteten Controls für jedes neue Release der Ausgangscontrols aktualisieren und zum zweiten ist es nicht gerade schön in einer bestehenden Anwendung alle Controls gegen die abgeleitete Variante austauschen zu müssen.

Deshalb möchte ich hier einen alternativen Weg vorstellen. Die Idee beruht darauf, dass die Controls, denen es an Accesibility fehlt jeweils in ein Panel platziert werden und auf diesem Panel dann die entsprechenden Patterns implementiert werden. Das Panel kann die Operationen dann an das Control in seinem Bauch weiterleiten.

image

Ich habe mal ein Beispiel für Janus Calendar Controls gebaut. Zunächst habe ich mir ein UIA-Panel erstellt, von dem ich dann die weiteren Panels für die spezifischen Controls ableiten kann.

   1: using System;
   2: using System.Drawing;
   3: using System.Security.Permissions;
   4: using System.Windows.Automation;
   5: using System.Windows.Automation.Provider;
   6: using System.Windows.Forms;
   7:  
   8: namespace WindowsFormsApplication1
   9: {
  10:     public partial class UIAPanel : Panel, IRawElementProviderSimple
  11:     {
  12:         public UIAPanel()
  13:         {
  14:             this.BackColor = Color.Yellow;
  15:             this.Height = 0;
  16:             this.Width = 0;
  17:             this.AutoSize = true;
  18:         }
  19:  
  20:         [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
  21:         protected override void WndProc(ref Message m)
  22:         {
  23:             // 0x3D == WM_GETOBJECT
  24:             Int32 param = 0;
  25:             if (Int32.TryParse(m.LParam.ToString(), out param))
  26:             {
  27:                 if ((m.Msg == 0x3D) && (param == AutomationInteropProvider.RootObjectId))
  28:                 {
  29:                     m.Result = AutomationInteropProvider.ReturnRawElementProvider(
  30:                         Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
  31:                     return;
  32:                 }
  33:             }
  34:             base.WndProc(ref m);
  35:         }
  36:  
  37:         #region IRawElementProviderSimple Members
  38:  
  39:         public object GetPatternProvider(int patternId)
  40:         {
  41:             if (patternId == ValuePatternIdentifiers.Pattern.Id)
  42:             {
  43:                 return this;
  44:             }
  45:             else
  46:             {
  47:                 return null;
  48:             }
  49:         }
  50:  
  51:         public object GetPropertyValue(int propertyId)
  52:         {
  53:             if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
  54:             {
  55:                 return "CalendarPanel";
  56:             }
  57:             else if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
  58:             {
  59:                 return ControlType.MenuBar.Id;
  60:             }
  61:  
  62:             if (propertyId == AutomationElementIdentifiers.HelpTextProperty.Id)
  63:             {
  64:                 return "Help for CalendarPanel";
  65:             }
  66:  
  67:             if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
  68:             {
  69:                 return this.Name; 
  70:             }
  71:  
  72:             if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
  73:             {
  74:                 return true;
  75:             }
  76:  
  77:             else
  78:             {
  79:                 return null;
  80:             }
  81:         }
  82:  
  83:         public IRawElementProviderSimple HostRawElementProvider
  84:         {
  85:             get
  86:             {
  87:                 return AutomationInteropProvider.HostProviderFromHandle(Handle);
  88:             }
  89:         }
  90:  
  91:         public ProviderOptions ProviderOptions
  92:         {
  93:             get
  94:             {
  95:                 return ProviderOptions.ServerSideProvider;
  96:             }
  97:         }
  98:  
  99:         #endregion
 100:  
 101:     }
 102: }

 

Dieses Panel stellt einen ServerSide Provider zur Verfügung. Wir können nun von diesem Control ableiten und ein entsprechendes Pattern, z.B. das SetValue Pattern implementieren:

   1: using System;
   2: using System.Windows.Automation.Provider;
   3: using System.Windows.Forms;
   4:  
   5: namespace WindowsFormsApplication1
   6: {
   7:     public partial class CalendarPanel : UIAPanel, IValueProvider
   8:     {
   9:         private Janus.Windows.Schedule.Calendar control;
  10:         public Janus.Windows.Schedule.Calendar Control
  11:         {
  12:             get
  13:             {
  14:                 if (control == null)
  15:                 {
  16:                     if (this.Controls.Count > 0 && this.Controls[0].GetType() == typeof(Janus.Windows.Schedule.Calendar))
  17:                         control = (Janus.Windows.Schedule.Calendar)this.Controls[0];
  18:                 }
  19:                 return control;
  20:             }
  21:         }
  22:  
  23:         #region IValueProvider Members
  24:  
  25:         public bool IsReadOnly
  26:         {
  27:             get 
  28:             {
  29:                 return false;
  30:             }
  31:         }
  32:  
  33:         public void SetValue(string value)
  34:         {
  35:             this.BeginInvoke((MethodInvoker)delegate()
  36:             {
  37:                 DateTime date = DateTime.Parse(value);
  38:                 Control.SelectionRange = new Janus.Windows.Schedule.DateRange(date, date);
  39:             });
  40:         }
  41:  
  42:         public string Value
  43:         {
  44:             get 
  45:             {
  46:                 return Control.SelectionRange.End.ToShortDateString(); 
  47:             }
  48:         }
  49:  
  50:         #endregion
  51:     }
  52: }

 

Wenn wir nun das Calendar_Control nicht direkt auf unserer Form platzieren, sondern in einem solchen CalendarPanel ablegen, können wir eine Automatisierung über die UIA gegen dieses Panel implementieren. Was nun noch optimiert werden soll, ist dass die ganzen Controls nicht händisch in die jeweiligen Panels platziert werden sollen, sondern dies soll nach Möglichkeit automatisiert werden. der Ansatz hierbei ist, dass alle Controls auf der Form beim Laden untersucht werden und für die gewünschten Controls dynamisch entsprechende Panels erzeugt werden sollen, in die dann die Controls platziert werden. Dieser Ansatz bietet zudem den Vorteil, dass man die UIA-Panels nur dann nutz, wenn man UI-Test ausführen möchte. Bei der Release-Version sind diese dann nicht enthalten. Zwar unterscheidet sich dadurch Release und Test-Version geringfügig, jedoch sollten diese Implikationen vernachlässigbar sein, vor allem dann, wenn beim Entwickeln komplett auf die Panels verzichtet wird und diese wirklich nur für die UI-Tests genutzt werden.

Der Code dazu sieht dann so aus:

   1: private void PlaceControlsIntoPanel(Control.ControlCollection controls)
   2: {
   3:     Panel uiaPanel;
   4:  
   5:     foreach (Control automationControl in controls.OfType<Control>().ToList())
   6:     {
   7:         switch (automationControl.GetType().ToString())
   8:         {
   9:             case "Janus.Windows.CalendarCombo.CalendarCombo":
  10:                 {
  11:                     uiaPanel = new CalendarComboPanel();
  12:                     break;
  13:                 }
  14:             case "Janus.Windows.Schedule.Calendar":
  15:                 {
  16:                     uiaPanel = new CalendarPanel();
  17:                     break;
  18:                 }
  19:             default:
  20:                 {
  21:                     if (automationControl.HasChildren)
  22:                     {
  23:                         PlaceControlsIntoPanel(automationControl.Controls);
  24:                     }
  25:                     continue;
  26:                 }
  27:         }
  28:         uiaPanel.Name = "p_" + automationControl.Name;
  29:         uiaPanel.Top = automationControl.Top;
  30:         uiaPanel.Left = automationControl.Left;
  31:         uiaPanel.Controls.Add(automationControl);
  32:         automationControl.Top = 0;
  33:         automationControl.Left = 0;
  34:         controls.Add(uiaPanel);
  35:     }
  36: }

 

Wird die Anwendung dann inkl. Test-Client ausgeführt, sieht das so aus:

image

Friday, July 03, 2009 2:10:58 PM (Mitteleuropäische Sommerzeit, UTC+02:00)  #    Comments [0] -
UI Automation
# Tuesday, November 11, 2008

Ok, not to confuse you, I'm not going to blog English from now on. But I did a session on UI-Testsing with the UI Automation Framework and to provide slides and demo-code to the attendees I use this blog post. The session went fairly well from my point of view and I had some really interesting discussions on this topic after the session. I hope those who have joined me got some good information about how you can do UI-testing today with completely free tools.

I'll provide some pictures later because I cannot read them from my camera right now.

Here comes the downloads:

Tuesday, November 11, 2008 4:27:49 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [1] -
UI Automation | Vorträge
# Friday, October 10, 2008

Mit Hilfe des UI Automation Frameworks (UIA) können Oberflächen aus einer Testanwendung heraus "ferngesteuert" werden. Damit lassen sich z.B. UI-Tests bauen. Die Grundlagen hierzu habe ich in zwei Blogposts bzw. Webcasts beschrieben.

MSDN WebCast zum UI-Recording
MSDN WebCast zum UI-Testing mit dem UI Automation Framework

Wenn man sich mit dieser Technologie etwas intensiver beschäftigt wird man früher oder später auf ein paar Probleme stoßen. So veröffentlichen beispielsweise nicht alle Third-Party-Controls alle erforderlichen Funktionalitäten über die UIAutomation Patterns. Und leider gibt es auch bei Standard-Winforms-Controls noch ein paar Lücken. Ein Beispiel hierzu ist das MenuStrip-Control. Dieses Control bietet leider nicht die erforderlichen Events um die Auswahl eines Menüeintrags aufzuzeichnen. Und für die Auswahl eines Menüeintrages gibt es auch kein geeignetes Invoke-Pattern o.ä.

Das schöne am UI Automation Framework ist jedoch, dass man diese Funktionalitäten selber nachrüsten kann. Dies wollen wir nun am Beispiel des MenuStrips mal durchspielen. Zum Einsatz kommen sog. Serverside Provider. Wir gehen zunächst her und erstellen uns ein eigenes Control das wir von MenuStrip und entsprechenden Interfaces aus dem UIA ableiten.

public class ExtendedMenuStrip : MenuStrip, IRawElementProviderSimple, IValueProvider

 

Über das Interface IValueProvider geben wir an, dass das Control das ValuePattern implementieren soll. Wir verwenden hier das ValuePattern statt des InvokePatterns da wir ja angeben müssen, welches Menüelement aufgerufen werden soll.

Zunächst werden wir das Interface IRawElementProviderSimple implementieren.

   1: #region IRawElementProviderSimple Members
   2: public object GetPatternProvider(int patternId)
   3: {
   4:     if (patternId == ValuePatternIdentifiers.Pattern.Id)
   5:     {
   6:         return this;
   7:     }
   8:     else
   9:     {
  10:         return null;
  11:     }
  12: }
  13:  
  14: /// <summary>
  15: /// Get the value of properties
  16: /// </summary>
  17: /// <param name="propertyId"></param>
  18: /// <returns></returns>
  19: public object GetPropertyValue(int propertyId)
  20: {
  21:     if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
  22:     {
  23:         return "ExtendedMenuStrip";
  24:     }
  25:     else if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
  26:     {
  27:         return ControlType.MenuBar.Id;
  28:     }
  29:  
  30:     if (propertyId == AutomationElementIdentifiers.HelpTextProperty.Id)
  31:     {
  32:         return "Help for ExtendedMenuStrip";
  33:     }
  34:  
  35:     if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
  36:     {
  37:         return this.Name;
  38:     }
  39:  
  40:     if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
  41:     {
  42:         return true;
  43:     }
  44:  
  45:     if (propertyId == AutomationElementIdentifiers.ItemStatusProperty.Id)
  46:     {
  47:         return SelectedItemID;
  48:     }
  49:  
  50:     else
  51:     {
  52:         return null;
  53:     }
  54: }
  55:  
  56: /// <summary>
  57: /// Get the host rawelement provider
  58: /// </summary>
  59: public IRawElementProviderSimple HostRawElementProvider
  60: {
  61:     get
  62:     {
  63:         return AutomationInteropProvider.HostProviderFromHandle(Handle);
  64:     }
  65: }
  66:     
  67: /// <summary>
  68: /// Get the provider options
  69: /// </summary>
  70: public ProviderOptions ProviderOptions
  71: {
  72:     get
  73:     {
  74:         return ProviderOptions.ServerSideProvider;
  75:     }
  76: }
  77:  
  78: #endregion

 

Die Methode GetPatternProvider gibt das Objekt selbst zurück, wenn ein Provider für ein ValuePattern angefordert wird. Da unser Control nur dieses Pattern implementiert, reagiert die Funktion nur auf dieses Pattern. Wir können das Control selbst zurückgeben, da dieses ja das ValuePattern implementiert. Altzernativ könnte man natürlich auch einen expliziten Provider definieren und hier zurückgeben. Die Methode GetPropertyValue gibt je nach übergebenen PropertyID einen entsprechenden Wert zurück. Hier wird z.B. die ID des Controls als AutomationID zurückgegeben. Die beiden Properties HostRawElementProvider und ProviderOptions sind readonly und geben einen Hostprovider bzw. den Typ der Providers zurück.

Die Implementierung des IValueProvider Interface ist ebenfalls recht einfach:

   1: #region IValueProvider Members       
   2: /// <summary>
   3: /// Get readonly as false
   4: /// </summary>
   5: public bool IsReadOnly
   6: {
   7:     get
   8:     {
   9:         return false;
  10:     }
  11: }
  12:  
  13: /// <summary>
  14: /// Set value: Invoke the click event of the item
  15: /// </summary>
  16: /// <param name="value"></param>
  17: public void SetValue(string value)
  18: {
  19:     object[] param = new object[1];
  20:     param[0] = null;
  21:     ToolStripMenuItem toolStripMenuItem = getToolStripMenuItemByName(this.Items, value);
  22:     if (toolStripMenuItem != null)
  23:         toolStripMenuItem.GetType().GetMethod("OnClick", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(toolStripMenuItem, param);
  24: }
  25:  
  26: /// <summary>
  27: /// Get the ID of the selected item
  28: /// </summary>
  29: public string Value
  30: {
  31:     get
  32:     {
  33:         return SelectedItemID;
  34:     }
  35: }
  36: #endregion

 

Entscheidend ist hier die Methode SetValue. Hier ermitteln wir das entsprechende Element innerhalb des MenuStrips (ToolStripMenuItem) und rufen hier einen Click-Event auf. Da die OnClick Methode nicht public ist, müssen wir hier Reflection verwenden um diese aufrufen zu können (siehe auch Events von WinForms Controls von Außen aufrufen). Das Aufrufen des Events ist an dieser Stelle notwendig, da ja mehrere Eventhandler registriert sein können und die wollen wir alle aufrufen. Durch den Click-Event verhält sich das Control am ähnlichsten zum Anklicken in der UI.

Nun brauchen wir noch einen Event der ausgelöst wird wenn ein Menüeintrag ausgewählt wird.

   1: /// <summary>
   2: /// Set eventhandlers
   3: /// </summary>
   4: /// <param name="items"></param>
   5: public void SetExtendedMenuStripEventHandlers(ToolStripItemCollection items)
   6: {
   7:     for (int i = 0; i <= items.Count - 1; i++)
   8:     {
   9:         ToolStripMenuItem toolStripMenuItem = items[i] as ToolStripMenuItem;
  10:         if(toolStripMenuItem != null)
  11:         {
  12:             toolStripMenuItem.Click += new EventHandler(ItemRaiseAutomationEvent);
  13:             SetExtendedMenuStripEventHandlers(toolStripMenuItem.DropDownItems);
  14:         }
  15:     }
  16: }
  17:  
  18: /// <summary>
  19: /// Raise automation event
  20: /// </summary>
  21: /// <param name="sender"></param>
  22: /// <param name="e"></param>
  23: private void ItemRaiseAutomationEvent(object sender, EventArgs e)
  24: {
  25:     if ((AutomationInteropProvider.ClientsAreListening))
  26:     {
  27:         AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);
  28:         this.SelectedItemID =((ToolStripMenuItem)sender).Name;
  29:         AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);
  30:     }
  31: }

 

Die Methode SetExtendedMenuStripEventHandlers registriert auf jedem ToolStripMenuItem einen EventHandler. Dieser löst dann den AutomationEvent aus. Hier übergeben wir im Feld SelectedItemID den Name des ToolStripMenuItems das angeklickt wurde.

Nun müssen wir noch die Methode wndProc überschreiben damit diese bei WM_GETOBJECT den entsprechenden AutomationProvider zurückgibt.

   1: /// <summary>
   2: /// Process Windows-based messages
   3: /// </summary>
   4: /// <param name="m"></param>
   5: [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
   6: protected override void WndProc(ref Message m)
   7: {
   8:     // 0x3D == WM_GETOBJECT
   9:     Int32 param = 0;
  10:     if (Int32.TryParse(m.LParam.ToString(), out param))
  11:     {
  12:         if ((m.Msg == 0x3D) && (param == AutomationInteropProvider.RootObjectId))
  13:         {
  14:             m.Result = AutomationInteropProvider.ReturnRawElementProvider(
  15:                 Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
  16:             return;
  17:         }
  18:     }
  19:     base.WndProc(ref m);
  20: }

Das so erstellte Control kann nun statt des normalen MenuStrip Controls verwendet werden und ist nun über UI Automation ansprechbar. Die fertige Solution inkl. dem UI-Recoder kann hier heruntergeladen werden:

Vielleicht noch kurz eine Anmerkung weshalb der Zugriff auf die ToolStripItems etwas umständlich gelöst wurde. Eigentlich wäre es doch einfach der direkt auf dem ToolStripMenuItem den Event auszulösen. Dann könnte der Recoder auch direkt einen Invoke auf dem ToolStripMenuItem generieren. Dazu müsste dann aber der ToolStripMenuItem abgeleitet werden und dann wird's aber mit der Designer-Unterstützung problematisch. Denn um das Control soweit umzubauen, dass im Designer beim Hinzufügen von ToolStripMenuItems zum MenuStrip der abgeleitete Typ verwendet wird ist ein deutlich höherer Aufwand notwendig als das Ganze über den MenuStrip laufen zu lassen.

Friday, October 10, 2008 12:43:23 AM (Mitteleuropäische Sommerzeit, UTC+02:00)  #    Comments [0] -
UI Automation
# Thursday, July 24, 2008

MSDN Webcasts

Zusammen mit Christian Binder habe ich nun einen MSDN Webcast aufgenommen der sich mit dem Thema UI Events und Record & Play für UI Testing beschäftigt. Dieser Webcast ist als Fortsetzung zum Thema UI Testing mit dem UI Automation Framework aufgebaut. Der Webcast selbst wird als Download ab dem 08.08.08 (cooles Datum) verfügbar sein. Den Demo_Code zum Webcast kann man ab sofort hier runterladen.

Kurz hier der Inhalt des Webcasts zusammengefasst.

Über das UI Automation Framework kann man Events aus einer Anwendung abfangen. Diese Events kann man zunächst am besten mit dem UISpy untersuchen. Nun kann man einen kleinen Recorder erstellen, der diese Events nutzt, um Benutzereingaben in einer Anwendung zu erkennen und automatisiert Code erstellt, der diese Benutzereingaben simuliert. Dieser Code kann nun in einem Unit-Test genutzt werden um automatisiert im Rahmen eines Testdurchlaufes die Benutzerinteraktion zu wiederholen. Der Clou dabei ist, dass sogar die Asserts für die erwarteten Ergebnisse automatisiert aufgezeichnet werden können.

 

Download Demo-Code 

Download Webcast

Thursday, July 24, 2008 10:18:50 PM (Mitteleuropäische Sommerzeit, UTC+02:00)  #    Comments [0] -
Qualitätsmanagement | UI Automation | Vorträge
# Thursday, May 15, 2008

Durch unseren gestrigen Vortrag zu Rosario bin ich auf einen Punkt aufmerksam geworden, der mir zum Thema UI Automation so noch gar nicht bewusst war. Man kann mit UI-Automation auch Web-Anwendungen steuern. Das ist eine echt coole Geschichte.

Am einfachsten verwendet man den UI-Spy um sich mal anzusehen, was da geht. Also IE auf und mal reinschauen. UI-Spy mit "Run as Administrator" starten, damit wir die Pattern anwenden können. Dann zunächst mal den Hovering mode aktivieren:

image

Nun kann man auf der Web-Seite mit der Maus auf einen Link fahren und die CTRL-Taste drücken. Es dauert einen kurzen Moment, dann wir der Link mit einem roten Kästchen umrahmt und UI Spy zeigt das entsprechende Objekt im Baum an.

image

image

Es empfiehlt sich nun, den Hovering mode wieder abzuschalten, da sonst jedes weiteres Drücken der CTRL-Taste den Scope ändern würde. Nun können wir uns die verfügbaren Patterns für das Element anschauen. Dazu einfach mit der rechten Maustaste auf den Hyperlink und dann "Control Patterns auswählen". Uns interessiert beim Hyperlink das Invoke-Pattern das Aktionen wie Click etc. ausführt.

image

Wenn man nun auf "Call Method" klicht, wird der Hyperlink aufgerufen, d.h. der IE verhält sich, wie wenn man den Hyperlink mit der Maus anklicken würde, d.h. er ruft die entsprechende Seite auf. Nun wollen wir uns noch ansehen, wie man auch Inhalte in Textboxen und andere Controls einfügen kann. Hierzu habe ich ein Formular im IE geöffnet und wieder mit dem UISpy das Element identifiziert. Nun kann man das ValuePlattern verwenden um den Value der Textbox zu editieren.

image

image

Mit diesem Wissen und dem Basiswissen aus meinem Webcast zur Erstellung von Unit-Test mit dem UI Automation Framework können wir nun Unit-Tests schreiben, die Web-Oberflächen automatisiert testen.

Bleibt die Frage wo ist der Unterschied zu den Web-Tests die mit VSTS Edition for Tester erstellt werden können? Der wichtigste Unterschied ist, dass die Web-Test sog. Wired-Level-Tests durchführen. D.h. diese Tests zeichnen die HTTP-Kommunikation zwischen Client und Server auf und können diese dann abspielen und in den HTTP-Paketen bestimmte Informationen validieren. Verwendet man nun aber z.B. AJAX, dann findet dort auch eine HTTP-Kommunikation statt, z.B. beim Aufruf eines Web-Services und der Web-Service liefert auch Daten zurück, ob die im Client dann aber per Java-Script richtig verarbeitet werden bleibt fraglich. Somit testen Web-Tests den Server, nicht den Client. Mit der UI-Automation werden die Tests direkt mit den Elementen der Clients ausgeführt, d.h. ich kann z.B. auch prüfen, ob die Daten, die ein Webservice zurückliefert an der Oberfläche korrekt dargestellt werden.

Der zweite Unterschied liegt einfach darin, dass das UI Automation Framework Bestandteil des .Net Frameworks 3.0 und somit kostenlos ist wohingegen für die Erstellung von Web-Tests ein VSTS for Tester notwendig ist.

Nachtrag:

Einen Punkt muss ich zu dem oben geschriebenen noch klarstellen. Das Problem für UI-Tests mit dieser Methode ist, dass die Web-Controls keine AutomationID veröffentlichen. Somit gibt es keine Möglichkeit diese entsprechend aus dem Automatisierungscode anzusprechen. Die Runtime-ID ist in diesem Fall leider nicht wirklich brauchbar. Ich hatte gehofft, dass der IE8 hier eine Verbesserung bringt, was aber leider nicht der Fall ist. Somit ist dies momentan leider nur ein schöner Ansatz, der in der Realität nicht recht funktioniert.

Thursday, May 15, 2008 8:11:00 PM (Mitteleuropäische Sommerzeit, UTC+02:00)  #    Comments [0] -
UI Automation
# Tuesday, April 22, 2008

MSDN Webcasts

Zusammen mit Christian Binder habe ich einen MSDN Webcast zum Thema UI-Testing mit dem UI Automation Framework erstellt. Der Webcast zeigt, wie man mit dem UI Automation Framework Anwendungen aus einer anderen Anwendung heraus steuern kann. Diese Methode eignet sich sehr gut um z.B. Unit-Tests zu erstellen, die die Oberfläche einer Anwendung testen. Damit lassen sich Oberflächentests sehr schön automatisieren. Und das beste ist, dass UI Automation Framework ist Bestandteil des .Net Framework 3.0 und damit kostenlos.

Download des Webcasts

Tuesday, April 22, 2008 11:28:00 AM (Mitteleuropäische Sommerzeit, UTC+02:00)  #    Comments [2] -
Eigene Tutorials | News | UI Automation
Archive
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012
Thomas
Sign In
Statistics
Total Posts: 560
This Year: 3
This Month: 1
This Week: 3
Comments: 351
Themes
All Content © 2012, Thomas
DasBlog theme 'Business' created by Christoph De Baene (delarou)