コントロールのコード化された 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 への参照を追加する必要があります。ビルド警告を除去するために、ユーザー補助の [相互運用機能型の埋め込み] を False に変更してください。アプリケーションの実行時にコンソール ウィンドウが表示されないように、プロジェクトの出力の種類を [コンソール アプリケーション] から [Windows アプリケーション] に変更できます。
プロパティ プロバイダーの実装によってカスタム プロパティの検証をサポートする
記録と再生およびプロパティの検証の基本的なサポートを実装したら、UITestPropertyProvider プラグインを実装して、コントロールのカスタム プロパティをコード化された UI テストから使用できるようにすることができます。たとえば、次のプロシージャでは、コード化された UI テストがグラフ コントロールの CurveLegend 子コントロールの State プロパティにアクセスできるようにするプロパティ プロバイダーを作成します。
カスタム プロパティの検証をサポートするには
曲線の凡例のアクセス可能なオブジェクトの 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(); } } }
クラス ライブラリ プロジェクトを作成し、ユーザー補助、Microsoft.VisualStudio.TestTools.UITesting、Microsoft.VisualStudio.TestTools.UITest.Common、および Microsoft.VisualStudio.TestTools.Extension への参照を追加して、コントロール用の UI テスト拡張パッケージを作成します。ユーザー補助の [相互運用機能型の埋め込み] を 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; }
新しいクラスの PropertyNames メソッドの型を返すように、プロパティ プロバイダーの GetPropertyNamesClassType メソッドをオーバーライドします。
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 テスト ビルダーで、プロパティ プロバイダーを実行するためのアサートを作成し、操作フィルターを実行するための操作を記録します。