問題の声明
まず、作成したUIの検証プロセスを自動化するプログラマーが使用するために、次のメソッドが設計されていることを予約する必要があります。したがって、少なくとも基本的なプログラミングスキルを持たないQAチームにとっては、この記事はほとんど役に立たないでしょう。 また、UIのテストは決してユニットテストと見なすことはできず、TDDを介したコード作成サイクルに含めることはできず、ビルドビルド中に別のサーバーで実行することをお勧めします(もちろん、各コミット後) 。 なぜローカルではないのですか? それは非常に遅いため、いらいらし始め、しばらくして開発者はローンチ時にスコアを付けます。
たとえば、タスクは簡単です-2つのボタンを持つアプリケーションがあります。 フィールドの最初のテキストをクリックすると、特定のテキストが表示されます(「Habrahabr」とします)。 2番目をクリックすると、現在の日時が表示されます。
したがって、次の場合には少なくとも3つのテストが必要です。
- アプリケーションの起動時のテキストボックスの最初のテキスト。
- 最初のボタンをクリックした後のテキスト。
- 2番目のボタンをクリックした後のテキスト。
既存のソリューションの概要
1. .Net UIオートメーション
フレームワーク自体は、WPFのリリースとともにかなり前に登場しましたが、ブログで適切に取り上げられていませんでした。 UIオートメーションは、任意のWin32、Windows Forms、またはWPFアプリケーションのコントロールツリーの仮想化ライブラリであり、これらのコントロールのプロパティへの読み取りおよび書き込み用のアクセスが可能です。 入力イベントのエミュレートもサポートされています。 読者の1人がMicrosoft Active Accessibilityライブラリを使用していた場合、 UIオートメーションはほとんどその直接の子孫であることに注意してください。
このライブラリでは、各コントロールはAutomationElement型のオブジェクトとして表され、イベントの生成、プロパティの取得、および子要素の検索のためのメソッドを提供します。 アプリケーションのウィンドウの最初のAutomationElementオブジェクトは、 AutomationElement.FromHandleメソッド(process.MainWindowHandle)を使用して取得できます。processは、テスト中のアプリケーションのプロセスへのリンク、またはデスクトップを介して取得できます。
AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, " " ));
* This source code was highlighted with Source Code Highlighter .
特定のコントロールについて、 UIオートメーションは、 ExpandCollapsePatternやSelectionItemPatternなどのAutomationPatternsと呼ばれる追加のラッパーセットを提供します。これにより、これらのコントロールに固有の機能(エキスパンダーの展開/折りたたみ機能など)を使用できるようになります。
長所
- 無料です。
- Microsoftからのサポート。
- これは.netフレームワークの一部です。
- Win32、Windowsフォーム、およびWPFアプリケーションをテストする機能。
欠点
- 実行可能ファイルへのパスを指定する必要があるか、ウィンドウのタイトルに結び付ける必要はありません。 パスが変更され、タイトルが一意でない場合があります。
- 使用可能なプロパティの限られたセット-実際、Microsoftが仮想化したこれらのタイプのコントロールとプロパティのみを使用できます。 カスタマイズ(複雑な複合コントロールの使用など)があると、テストの記述プロセスが複雑になります。
- また、可能性のある欠点は、アプリケーションがサードパーティプロセスとして起動することです。そのため、システム内のオブジェクトの動作を突然変更または置換する必要がある場合、耳の対応するダンスが開始されます。 「そして、それを交換する必要はありません。テストは、システムがユーザーにとって機能するのと同じ形式でシステムをチェックする必要があります。」-あなたは言うが、一部は正しい。 ただし、重要な条件(特定の瞬間にファイルにアクセスすることを拒否)でシステムの動作を確認する必要がある場合、メモリフェースがないことを確認する、または少なくとも現在の日付の出力をテストする必要がある場合にのみ、「内部」システムにアクセスせずにこれを拒否します便利ではなく、長く、時には不可能です。
タスクのテストの例
アプリケーションの起動時のテキストボックスの最初のテキスト
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestStartup()
- {
- var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
- var process = Process.Start(appPath);
- 試してみる
- {
- Thread.Sleep(5000);
- var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
- var buttonControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Button));
- var textBoxControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Edit));
- var textBox =(ValuePattern)textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
- Assert.AreEqual( "123123123" 、textBox.Current.Value);
- }
- ついに
- {
- process.Kill();
- }
- }
最初のボタンをクリックした後のテキスト。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestMethodUIAutomation()
- {
- var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
- var process = Process.Start(appPath);
- 試してみる
- {
- Thread.Sleep(5000);
- var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
- var buttonControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Button));
- var textBoxControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Edit));
- var textBox =(ValuePattern)textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
- Assert.AreEqual( "123123123" 、textBox.Current.Value);
- var button =(InvokePattern)buttonControl.GetCurrentPattern(InvokePattern.Pattern);
- button.Invoke();
- Assert.AreEqual( "Habrahabr" 、textBox.Current.Value);
- }
- ついに
- {
- process.Kill();
- }
- }
2番目のボタンをクリックすると、現在の時刻が常に異なるため、テキストを実装できず、何もロックできません。
2.ホワイトプロジェクト
UIオートメーションに基づく無料のコーデックフレームワーク。 長所と短所は同じですが、コントロールツリーを操作するためのより便利で高度なAPIのみが異なります。
タスクのテストの例
アプリケーションの起動時のテキストボックスの最初のテキスト
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestStartup()
- {
- var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
- var application = White.Core.Application.Launch(appPath);
- Assert.IsNotNull(アプリケーション);
- var window = application.GetWindow( "MainWindow" );
- var textBox = window.Get <White.Core.UIItems.TextBox>();
- Assert.IsNotNull(textBox);
- Assert.AreEqual( "123123123" 、textBox.Text);
- }
最初のボタンをクリックした後のテキスト
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestWithWhite()
- {
- var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
- var application = White.Core.Application.Launch(appPath);
- Assert.IsNotNull(アプリケーション);
- var window = application.GetWindow( "MainWindow" );
- var textBox = window.Get <White.Core.UIItems.TextBox>();
- var button = window.Get <White.Core.UIItems.Button>(SearchCriteria.ByText( "クリックしてテスト" ));
- button.Click();
- Assert.AreEqual( "Habrahabr" 、textBox.Text);
- }
3. Visual Studio 2010のコード化されたUIテスト
コード化されたUI -Microsoftのソリューション。2010スタジオに登場し、繰り返し説明されてきました 。たとえばハブやhereなど 。
長所
- テストの自動生成のためのユーザーアクションを記録するレコーダーの存在。
- Visual Studioの "すぐに使える"の提供。
- Microsoftのサポート、TFSへの統合。
- 画面座標を参照せずに、コントロールツリーレベルで作業する機能。
欠点
一般に、一連の欠点はUI Automationの欠点と同じです。 別途、仕事をする機会は2010スタジオの特定のバージョン(Ultimate、Premium、Professional)のみであることを強調する必要があります。 さらに、3つすべてでテストの起動が可能な場合、プロジェクト内の対応するタイプのアイテムの作成とレコーダーの起動は、UltimateおよびPremiumのバージョンでのみ可能です。 そして
テストコードは自動生成されるため、特に興味がないため、テストコードは提供しません。
4.テスト完了など
テスト完了などのシステムは、1つのグループに一般化できます。 それらを詳細に説明しません。 これは別の記事のトピックです。いくつかのポイントのみを強調します。 その主な利点は、幅広いアプリケーション、プログラミングスキルが不要なこと、テストの作成、保存、サポートにVisual Studioを必要としない別のシステムが存在することです。 欠点は以前の決定で繰り返されます-支払い、濡れる可能性のない「ブラックボックス」としてのテスト対象システムに対する態度、およびテスト完了にはテストを実行するための独自の環境があるため、通常のmstestの使用は機能しません。
自分で何かをしましょう
WPFでアプリケーションのUIを記述している場合、 VisualTreeHelperクラスを使用してテストできます。 アルゴリズムは非常に単純です。テストメソッドの個別のテストフローでアプリケーションを実行し、 VisualTreeHelperを使用して必要な制御を取得し、イベントをエミュレートし、アサーションの値を読み取ります。
より簡単にテストを作成するために、私自身は日常的なアクションのパフォーマンスを単純化する小さなユーティリティクラスを作成しました。
アプリケーションの起動
var application = UI.Run(() => new App { MainWindow = new MainWindow() });
* This source code was highlighted with Source Code Highlighter .
コントロールのプロパティの取得。 説明します それらが作成されたスレッドだけがコントロールを操作できます;あなたは耳でそのような気絶をしなければなりません。
var window = application.Get(x => x.MainWindow);
* This source code was highlighted with Source Code Highlighter .
さて、ツリーで検索
var textBox = _mainWindow.FindChild((TextBox el) => el.Name == "SomeText" );
* This source code was highlighted with Source Code Highlighter .
それでも、光、色、その他の組成物を確認する必要があります。 その後、 RenderTargetBitmapを介して、比較のために古い方法でコントロールイメージを取得できます。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- private void AssertRender( string expectImageName、FrameworkElement elementForTest)
- {
- var image = elementForTest.Render();
- var expectPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory、expectImageName);
- if (!( File .Exists(expectPath)&& File .ReadAllBytes(expectPath).SequenceEqual(image)))
- {
- File .WriteAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory、 "fail_" + expectImageName)、image);
- 新しい AssertFailedExceptionをスロー ( string .Format( "イメージ '{1}'と等しくない要素{0}" 、elementForTest.Get(x => x.Name)、expectImageName));
- }
- }
- AssertRender( "button.png" 、ボタン);
長所
- 無料です。
- 追加のライブラリをインストールする必要はありません。
- システムの部品を濡らすことができます。
欠点
- テスト用のレコーダーはありません。 すべてを手書きで書きます。
- テストサポートは完全に肩にかかっています。
タスクのテストの例
アプリケーションの起動時のテキストボックスの最初のテキスト
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestStartup()
- {
- var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
- var mainWindow = application.Get(x => x.MainWindow);
- var textBox = mainWindow.FindChild((TextBox el)=> el.Name == "SomeTexBox" );
- Assert.IsNotNull(textBox);
- Assert.AreEqual( "123123123" 、textBox.Get(x => x.Text));
- application.Invoke(x => x.Shutdown());
- }
最初のボタンをクリックした後のテキスト
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- public void TestFirstButtonClick()
- {
- var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
- var mainWindow = application.Get(x => x.MainWindow);
- var textBox = mainWindow.FindChild((TextBox el)=> el.Name == "SomeTexBox" );
- var button = mainWindow.FindChild((Button el)=> el.Content.Equals( "クリックしてテスト" ));
- button.Raise(ButtonBase.ClickEvent);
- Assert.AreEqual( "Habrahabr" 、textBox.Get(x => x.Text));
- application.Invoke(x => x.Shutdown());
- }
2番目のボタンをクリックした後のテキスト。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- [テスト方法]
- [HostType( "Moles" )]
- public void TestSecondButton()
- {
- var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
- var mainWindow = application.Get(x => x.MainWindow);
- var dateTimeExpect = new DateTime (2011、12、08、12、30、25);
- MDateTime.NowGet =()=> dateTimeExpect;
- var button = mainWindow.FindChild((Button el)=> el.Content.Equals( "クリックしてテスト2" ));
- button.Raise(ButtonBase.ClickEvent);
- var textBox = mainWindow.FindChilds <TextBox>().First();
- Assert.AreEqual(dateTimeExpect.ToString()、textBox.Get(x => x.Text));
- application.Invoke(x => x.Shutdown());
- }
ここでは、 Molesフレームワークを使用してDateTime.Nowの呼び出しをブロックしました。その概要は、たとえばここで確認できます 。
もちろん、アプリケーションを作成し、そこからウィンドウを取得することをテストクラスの静的コンストラクターに導入して、テストスイートの合格とコードの簡素化にかかる時間を短縮することができます。
おわりに
UIテストの自動化にどの方法を選択するかは、現在のプロジェクトの特定の現実とあなた自身の経験に基づいて決定する必要があります。 UIのテストを作成するための基本的な方法を強調してみましたが、UIの利点と欠点を試してみる機会がありました。 この記事がお役に立てば幸いです。
参照資料
.Net UIオートメーション
ホワイトプロジェクト
Visual Studio 2010コード化されたUIテスト
テスト完了
ほくろ
テスト対象アプリケーションのソース+ VisualTreeHelperを使用したサンプルテスト (日付でテストを実行するには、Moleをインストールする必要があります)