啟用控制項的自動程式碼 UI 測試功能
如果您實作自動程式碼 UI 測試架構的支援,就可以更輕鬆地測試您的控制項。您可以用累加方式加入不斷增加的支援層級。您可以從支援錄製和播放以及屬性驗證開始。您可以依此為建置基礎,讓自動程式碼 UI 測試產生器能夠辨識控制項的自訂屬性,並提供自訂類別,以從產生的程式碼存取那些屬性。您也可以協助自動程式碼 UI 測試產生器,以較接近所錄製動作之意圖的方式來擷取動作。
本主題內容:
藉由實作協助工具,支援錄製和播放以及屬性驗證
自動程式碼 UI 測試產生器會擷取它在錄製期間遇到之控制項的相關資訊,然後產生程式碼,以重新執行該工作階段。如果您的控制項不支援協助工具,自動程式碼 UI 測試產生器將會使用螢幕座標來擷取動作 (例如滑鼠點按)。播放測試時,所產生的程式碼就會在相同的螢幕座標中發出這些滑鼠點按動作。如果在播放測試時,您的控制項出現在螢幕上的不同位置,所產生的程式碼將無法在您的控制項上執行該動作。如果是在不同的螢幕組態上、在不同環境中,或是在 UI 配置有所變更之後播放測試,這樣會導致失敗。
不過,如果您實作協助工具,當自動程式碼 UI 測試產生器錄製測試並產生程式碼時,將會使用該協助工具來擷取控制項的相關資訊。然後,當您執行測試時,即使控制項是在使用者介面中的其他地方,所產生的程式碼還是會對您的控制項重新執行這些事件。測試作者也可以使用控制項的基本屬性來建立判斷提示。
支援錄製和播放、屬性驗證,以及巡覽 Windows 表單控制項
依照下列程序的概述,以及 AccessibleObject 的詳細說明,為您的控制項實作協助工具。
實作衍生自 ControlAccessibleObject 的類別,並覆寫 AccessibilityObject 屬性,以傳回您的類別物件。
public partial class ChartControl : UserControl { // Overridden to return the custom AccessibleObject for the control. protected override AccessibleObject CreateAccessibilityInstance() { return new ChartControlAccessibleObject(this); } // Inner class ChartControlAccessibleObject represents accessible information // associated with the ChartControl and is used when recording tests. public class ChartControlAccessibleObject : ControlAccessibleObject { ChartControl myControl; public ChartControlAccessibleObject(ChartControl ctrl) : base(ctrl) { myControl = ctrl; } } }
覆寫可存取物件的 Role、State、GetChild 和 GetChildCount 屬性和方法。
為子控制項實作另一個協助工具物件,並覆寫子控制項的 AccessibilityObject 屬性以傳回該協助工具物件。
針對子控制項的協助工具物件,覆寫 Bounds、Name、Parent、Role、State、Navigate 和 Select 屬性和方法。
注意事項 |
---|
本主題一開始在此程序的 AccessibleObject 中提供協助工具範例,然後在其餘程序中,以其做為建置基礎。如果您想要建立協助工具範例的有效版本,請建立主控台應用程式,然後將 Program.cs 中的程式碼取代成該範例程式碼。您必須將參考加入協助工具、System.Drawing 和 System.Windows.Forms。您應將協助工具的 [內嵌 Interop 類型] 變更為 False,以消除建置警告。您可以將專案的輸出類型從主控台應用程式變更為 Windows 應用程式,如此當您執行應用程式時,才不會出現主控台視窗。 |
藉由實作屬性提供者,支援自訂屬性驗證
為記錄和播放以及屬性驗證實作基本支援之後,就可以藉由實作 UITestPropertyProvider 外掛程式,讓自動程式碼 UI 測試能夠使用控制項的自訂屬性。例如,下列程序所建立的屬性提供者,能夠讓自動程式碼 UI 測試存取圖表控制項的 CurveLegend 子控制項的狀態屬性。
支援自訂屬性驗證
覆寫曲線圖例可存取物件的 Description 屬性,以描述字串來傳遞豐富的屬性值 (以分號 (;) 將其與主要描述分隔,如果您要實作多個屬性,也是以分號將彼此分隔)。
public class CurveLegendAccessibleObject : AccessibleObject { // add the state property value to the description public override string Description { get { // Add “;” and the state value to the end // of the curve legend’s description return "CurveLegend; " + State.ToString(); } } }
藉由建立類別庫專案來建立控制項的 UI 測試擴充套件,並將參考加入協助工具、Microsoft.VisualStudio.TestTools.UITesting、Microsoft.VisualStudio.TestTools.UITest.Common 和 Microsoft.VisualStudio.TestTools.Extension。將協助工具的 [內嵌 Interop 類型] 變更為 False。
加入衍生自 UITestPropertyProvider 的屬性提供者類別。
using System; using System.Collections.Generic; using Accessibility; using Microsoft.VisualStudio.TestTools.UITesting; using Microsoft.VisualStudio.TestTools.UITest.Extension; using Microsoft.VisualStudio.TestTools.UITesting.WinControls; using Microsoft.VisualStudio.TestTools.UITest.Common; namespace ChartControlExtensionPackage { public class ChartControlPropertyProvider : UITestPropertyProvider { } }
在 Dictionary 中放置屬性名稱和屬性描述元,以實作屬性提供者。
// Define a map of property descriptors for CurveLegend private static Dictionary<string, UITestPropertyDescriptor> curveLegendPropertiesMap = null; private static Dictionary<string, UITestPropertyDescriptor> CurveLegendPropertiesMap { get { if (curveLegendPropertiesMap == null) { UITestPropertyAttributes read = UITestPropertyAttributes.Readable | UITestPropertyAttributes.DoNotGenerateProperties; curveLegendPropertiesMap = new Dictionary<string, UITestPropertyDescriptor> (StringComparer.OrdinalIgnoreCase); curveLegendPropertiesMap.Add("State", new UITestPropertyDescriptor(typeof(string), read)); } return curveLegendPropertiesMap; } } // return the property descriptor public override UITestPropertyDescriptor GetPropertyDescriptor(UITestControl uiTestControl, string propertyName) { return CurveLegendPropertiesMap[propertyName]; } // return the property names public override ICollection<string> GetPropertyNames(UITestControl uiTestControl) { if (uiTestControl.ControlType.NameEquals("Chart") || uiTestControl.ControlType.NameEquals("Text")) { // the keys of the property map are the collection of property names return CurveLegendPropertiesMap.Keys; } // this is not my control throw new NotSupportedException(); } // Get the property value by parsing the accessible description public override object GetPropertyValue(UITestControl uiTestControl, string propertyName) { if (String.Equals(propertyName, "State", StringComparison.OrdinalIgnoreCase)) { object[] native = uiTestControl.NativeElement as object[]; IAccessible acc = native[0] as IAccessible; string[] descriptionTokens = acc.accDescription.Split(new char[] { ';' }); return descriptionTokens[1]; } // this is not my control throw new NotSupportedException(); }
覆寫 UITestPropertyProvider.GetControlSupportLevel,以指出您的組件為您的控制項及其子系提供控制項特定的支援。
public override int GetControlSupportLevel(UITestControl uiTestControl) { // For MSAA, check the control type if (string.Equals(uiTestControl.TechnologyName, "MSAA", StringComparison.OrdinalIgnoreCase) && (uiTestControl.ControlType == "Chart"||uiTestControl.ControlType == "Text")) { return (int)ControlSupport.ControlSpecificSupport; } // This is not my control, so return NoSupport return (int)ControlSupport.NoSupport; }
覆寫 UITestPropertyProvider 的其餘抽象方法。
public override string[] GetPredefinedSearchProperties(Type specializedClass) { throw new NotImplementedException(); } public override Type GetSpecializedClass(UITestControl uiTestControl) { throw new NotImplementedException(); } public override Type GetPropertyNamesClassType(UITestControl uiTestControl) { throw new NotImplementedException(); } public override void SetPropertyValue(UITestControl uiTestControl, string propertyName, object value) { throw new NotImplementedException(); } public override string GetPropertyForAction(UITestControl uiTestControl, UITestAction action) { throw new NotImplementedException(); } public override string[] GetPropertyForControlState(UITestControl uiTestControl, ControlStates uiState, out bool[] stateValues) { throw new NotImplementedException(); }
加入衍生自 UITestExtensionPackage 的擴充套件類別。
using System; using Microsoft.VisualStudio.TestTools.UITesting; using Microsoft.VisualStudio.TestTools.UITest.Extension; using Microsoft.VisualStudio.TestTools.UITest.Common; namespace ChartControlExtensionPackage { internal class ChartControlExtensionPackage : UITestExtensionPackage { } }
定義組件的 UITestExtensionPackage 屬性。
[assembly: Microsoft.VisualStudio.TestTools.UITest.Extension.UITestExtensionPackage( "ChartControlExtensionPackage", typeof(ChartControlExtensionPackage.ChartControlExtensionPackage))] namespace ChartControlExtensionPackage { …
在擴充套件類別中,覆寫 UITestExtensionPackage.GetService,以在要求屬性提供者時,傳回屬性提供者類別。
internal class ChartControlExtensionPackage : UITestExtensionPackage { public override object GetService(Type serviceType) { if (serviceType == typeof(UITestPropertyProvider)) { if (propertyProvider == null) { propertyProvider = new ChartControlPropertyProvider(); } return propertyProvider; } return null; } private UITestPropertyProvider propertyProvider = null; }
覆寫 UITestExtensionPackage 的其餘抽象方法和屬性。
public override void Dispose() { } public override string PackageDescription { get { return "Supports coded UI testing of ChartControl"; } } public override string PackageName { get { return "ChartControl Test Extension"; } } public override string PackageVendor { get { return "Microsoft (sample)"; } } public override Version PackageVersion { get { return new Version(1, 0); } } public override Version VSVersion { get { return new Version(10, 0); } }
建置您的二進位檔,並將其複製到 %programfiles%\Common Files\Microsoft Shared\VSTT\10.0\UITestExtensionPackages。
注意事項 |
---|
這個擴充套件將會套用至 "Text" 類型的任何控制項。如果您要測試多個相同類型的控制項,您必須個別進行測試,並管理當您錄製測試時所要部署的擴充套件。 |
藉由實作類別來存取自訂屬性,支援程式碼產生
當自動程式碼 UI 測試產生器從工作階段錄製作業產生程式碼時,它會使用 UITestControl 類別來存取您的控制項。
UITestControl uIAText = this.UIItemWindow.UIChartControlWindow.UIAText;
Assert.AreEqual(this.AssertMethod3ExpectedValues.UIATextState, uIAText.GetProperty("State").ToString());
如果您已實作屬性提供者來提供控制項自訂屬性的存取權,您可以加入用來存取這些屬性的特定類別,如此可簡化所產生的程式碼。
ControlLegend uIAText = this.UIItemWindow.UIChartControlWindow.UIAText;
Assert.AreEqual(this.AssertMethod3ExpectedValues.UIATextState, uIAText.State);
加入用來存取控制項的特定類別
實作衍生自 WinControl 的類別,並將控制項的類型加入建構函式中的搜尋屬性集合。
public class CurveLegend:WinControl { public CurveLegend(UITestControl c) : base(c) { // The curve legend control is a “text” type of control SearchProperties.Add( UITestControl.PropertyNames.ControlType, "Text"); } }
實作控制項的自訂屬性,以做為類別的屬性。
public virtual string State { get { return (string)GetProperty("State"); } }
覆寫屬性提供者的 UITestPropertyProvider.GetSpecializedClass 方法,以針對曲線圖例子控制項,傳回新類別的類型。
public override Type GetSpecializedClass(UITestControl uiTestControl) { if (uiTestControl.ControlType.NameEquals("Text")) { // This is text type of control. For my control, // that means it’s a curve legend control. return typeof(CurveLegend); } // this is not a curve legend control return null; }
覆寫屬性提供者的 GetPropertyNamesClassType 方法,以傳回新類別的 PropertyNames 方法的類型。
public override Type GetPropertyNamesClassType(UITestControl uiTestControl) { if (uiTestControl.ControlType.NameEquals("Text")) { // This is text type of control. For my control, // that means it’s a curve legend control. return typeof(CurveLegend.PropertyNames); } // this is not a curve legend control return null; }
藉由實作動作篩選,支援意圖感知動作
當 Visual Studio 錄製測試時,它會擷取每個滑鼠和鍵盤事件。不過,在某些情況下,動作的意圖可能會在一系列的滑鼠和鍵盤事件中遺失。比方說,如果您的控制項支援自動完成,在不同的環境中播放測試時,同一組滑鼠和鍵盤事件可能會導致不同的值。您可以加入動作篩選外掛程式,將一系列鍵盤和滑鼠事件取代成單一動作。如此一來,您就可以將導致選取某個值的一系列滑鼠和鍵盤事件,取代成設定該值的單一動作。這麼做可防止自動程式碼 UI 測試在不同環境之間所產生的自動完成差異。
支援意圖感知動作
實作衍生自 UITestActionFilter 的動作篩選類別,覆寫下列屬性:ApplyTimeout、Category、Enabled、FilterType、Group 和 Name。
internal class MyActionFilter : UITestActionFilter { // If the user actions we are aggregating exceeds the time allowed, // this filter is not applied. (The timeout is configured when the // test is run.) public override bool ApplyTimeout { get { return true; } } // Gets the category of this filter. Categories of filters // are applied in priority order. public override UITestActionFilterCategory Category { get { return UITestActionFilterCategory.PostSimpleToCompoundActionConversion; } } public override bool Enabled { get { return true; } } public override UITestActionFilterType FilterType { // This action filter operates on a single action get { return UITestActionFilterType.Unary; } } // Gets the name of the group to which this filter belongs. // A group can be enabled/disabled using configuration file. public override string Group { get { return "ChartControlActionFilters"; } } // Gets the name of this filter. public override string Name { get { return "Convert Double-Click to Single-Click"; } }
覆寫 ProcessRule。此範例會將按兩下動作取代成按一下動作。
public override bool ProcessRule(IUITestActionStack actionStack) { if (actionStack.Count > 0) { MouseAction lastAction = actionStack.Peek() as MouseAction; if (lastAction != null) { if (lastAction.UIElement.ControlTypeName.Equals( ControlType.Text.ToString(), StringComparison.OrdinalIgnoreCase)) { if(lastAction.ActionType == MouseActionType.DoubleClick) { // Convert to single click lastAction.ActionType = MouseActionType.Click; } } } } // Do not stop aggregation return false; }
將動作篩選加入擴充套件的 GetService 方法。
public override object GetService(Type serviceType) { if (serviceType == typeof(UITestPropertyProvider)) { if (propertyProvider == null) { propertyProvider = new PropertyProvider(); } return propertyProvider; } else if (serviceType == typeof(UITestActionFilter)) { if (actionFilter == null) { actionFilter = new RadGridViewActionFilter(); } return actionFilter; } return null; }
建置您的二進位檔,並將其複製到 %ProgramFiles%\Common Files\Microsoft Shared\VSTT\10.0\UITestExtensionPackages。
注意事項 |
---|
動作篩選與協助工具實作或屬性提供者沒有相依關係。 |
偵錯屬性提供者或動作篩選
內容提供者和動作篩選會在一個擴充套件中實作,自動程式碼 UI 測試產生器會在不同於您的應用程式的個別處理序中,載入及執行此擴充套件。
偵錯內容提供者或動作篩選
建置擴充套件的偵錯版本,將 .dll 和.pdb 檔案複製到 %ProgramFiles%\Common Files\Microsoft Shared\VSTT\10.0\UITestExtensionPackages。
執行您的應用程式 (不在偵錯工具中)。
執行自動程式碼 UI 測試產生器。
codedUITestBuilder.exe /standalone
將偵錯工具附加至 codedUITestBuilder 處理序。
在您的程式碼中設定中斷點。
在自動程式碼 UI 測試產生器中,建立判斷提示來運作您的屬性提供者,並錄製動作來運作您的動作篩選。
外部資源
指引
使用 Visual Studio 2012 測試持續傳遞 - 第 2 章:單元測試:測試內部 (英文)