デスクトップユーザーインターフェイスをテストする自動テストを作成する

近年、単体テスト、回帰テスト、継続的インテグレーション、TDD、BDDなどのトピックが一般的になり、これらの手法をプロジェクトで積極的に使用する開発者が増えています。 同時に、別の問題はデスクトップアプリケーションのユーザーインターフェイスの自動テストの問題です。 この記事では、既存のソリューションを検討するとともに、.netでUIのテストを書く自転車のバリエーションを紹介します。



問題の声明



まず、作成したUIの検証プロセスを自動化するプログラマーが使用するために、次のメソッドが設計されていることを予約する必要があります。したがって、少なくとも基本的なプログラミングスキルを持たないQAチームにとっては、この記事はほとんど役に立たないでしょう。 また、UIのテストは決してユニットテストと見なすことはできず、TDDを介したコード作成サイクルに含めることはできず、ビルドビルド中に別のサーバーで実行することをお勧めします(もちろん、各コミット後) 。 なぜローカルではないのですか? それは非常に遅いため、いらいらし始め、しばらくして開発者はローンチ時にスコアを付けます。



たとえば、タスクは簡単です-2つのボタンを持つアプリケーションがあります。 フィールドの最初のテキストをクリックすると、特定のテキストが表示されます(「Habrahabr」とします)。 2番目をクリックすると、現在の日時が表示されます。



したがって、次の場合には少なくとも3つのテストが必要です。

  1. アプリケーションの起動時のテキストボックスの最初のテキスト。
  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オートメーションは、 ExpandCollapsePatternSelectionItemPatternなどのAutomationPatternsと呼ばれる追加のラッパーセットを提供します。これにより、これらのコントロールに固有の機能(エキスパンダーの展開/折りたたみ機能など)を使用できるようになります。



長所
欠点


タスクのテストの例


アプリケーションの起動時のテキストボックスの最初のテキスト





  1. [テスト方法]
  2. public void TestStartup()
  3. {
  4. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
  5. var process = Process.Start(appPath);
  6. 試してみる
  7. {
  8. Thread.Sleep(5000);
  9. var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
  10. var buttonControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Button));
  11. var textBoxControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Edit));
  12. var textBox =(ValuePattern)textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
  13. Assert.AreEqual( "123123123" 、textBox.Current.Value);
  14. }
  15. ついに
  16. {
  17. process.Kill();
  18. }
  19. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


最初のボタンをクリックした後のテキスト。





  1. [テスト方法]
  2. public void TestMethodUIAutomation()
  3. {
  4. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
  5. var process = Process.Start(appPath);
  6. 試してみる
  7. {
  8. Thread.Sleep(5000);
  9. var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
  10. var buttonControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Button));
  11. var textBoxControl = mainWindow.FindFirst(TreeScope.Children、 新しい PropertyCondition(AutomationElement.ControlTypeProperty、ControlType.Edit));
  12. var textBox =(ValuePattern)textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
  13. Assert.AreEqual( "123123123" 、textBox.Current.Value);
  14. var button =(InvokePattern)buttonControl.GetCurrentPattern(InvokePattern.Pattern);
  15. button.Invoke();
  16. Assert.AreEqual( "Habrahabr" 、textBox.Current.Value);
  17. }
  18. ついに
  19. {
  20. process.Kill();
  21. }
  22. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


2番目のボタンをクリックすると、現在の時刻が常に異なるため、テキストを実装できず、何もロックできません。



2.ホワイトプロジェクト



UIオートメーションに基づく無料のコーデックフレームワーク。 長所と短所は同じですが、コントロールツリーを操作するためのより便利で高度なAPIのみが異なります。



タスクのテストの例


アプリケーションの起動時のテキストボックスの最初のテキスト





  1. [テスト方法]
  2. public void TestStartup()
  3. {
  4. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
  5. var application = White.Core.Application.Launch(appPath);
  6. Assert.IsNotNull(アプリケーション);
  7. var window = application.GetWindow( "MainWindow" );
  8. var textBox = window.Get <White.Core.UIItems.TextBox>();
  9. Assert.IsNotNull(textBox);
  10. Assert.AreEqual( "123123123" 、textBox.Text);
  11. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


最初のボタンをクリックした後のテキスト





  1. [テスト方法]
  2. public void TestWithWhite()
  3. {
  4. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly()。Location)、 @ ".. \ .. \ .. \ TestUI \ bin \ Debug \ TestUI.exe" );
  5. var application = White.Core.Application.Launch(appPath);
  6. Assert.IsNotNull(アプリケーション);
  7. var window = application.GetWindow( "MainWindow" );
  8. var textBox = window.Get <White.Core.UIItems.TextBox>();
  9. var button = window.Get <White.Core.UIItems.Button>(SearchCriteria.ByText( "クリックしてテスト" ));
  10. button.Click();
  11. Assert.AreEqual( "Habrahabr" 、textBox.Text);
  12. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


3. Visual Studio 2010のコード化されたUIテスト



コード化されたUI -Microsoftのソリューション。2010スタジオに登場し、繰り返し説明されてきました 。たとえばハブやhereなど



長所
欠点


一般に、一連の欠点はUI Automationの欠点と同じです。 別途、仕事をする機会は2010スタジオの特定のバージョン(Ultimate、Premium、Professional)のみであることを強調する必要があります。 さらに、3つすべてでテストの起動が可能な場合、プロジェクト内の対応するタイプのアイテムの作成とレコーダーの起動は、UltimateおよびPremiumのバージョンでのみ可能です。 そして、torrentから自宅プロジェクトのUltimateバージョンを購入できる場合、そして数十人の開発者のライセンスに関しては、商業プロジェクトの場合、そのようなステップは、より高い当局と会計の側の誤解に出くわすかもしれません。



テストコードは自動生成されるため、特に興味がないため、テストコードは提供しません。



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を介して、比較のために古い方法でコントロールイメージを取得できます。





  1. private void AssertRender( string expectImageName、FrameworkElement elementForTest)
  2. {
  3. var image = elementForTest.Render();
  4. var expectPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory、expectImageName);
  5. if (!( File .Exists(expectPath)&& File .ReadAllBytes(expectPath).SequenceEqual(image)))
  6. {
  7. File .WriteAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory、 "fail_" + expectImageName)、image);
  8. 新しい AssertFailedExceptionをスローstring .Format( "イメージ '{1}'と等しくない要素{0}" 、elementForTest.Get(x => x.Name)、expectImageName));
  9. }
  10. }
  11. AssertRender( "button.png" 、ボタン);
*このソースコードは、 ソースコードハイライターで強調表示されました。


長所
欠点
タスクのテストの例


アプリケーションの起動時のテキストボックスの最初のテキスト





  1. [テスト方法]
  2. public void TestStartup()
  3. {
  4. var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
  5. var mainWindow = application.Get(x => x.MainWindow);
  6. var textBox = mainWindow.FindChild((TextBox el)=> el.Name == "SomeTexBox" );
  7. Assert.IsNotNull(textBox);
  8. Assert.AreEqual( "123123123" 、textBox.Get(x => x.Text));
  9. application.Invoke(x => x.Shutdown());
  10. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


最初のボタンをクリックした後のテキスト





  1. [テスト方法]
  2. public void TestFirstButtonClick()
  3. {
  4. var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
  5. var mainWindow = application.Get(x => x.MainWindow);
  6. var textBox = mainWindow.FindChild((TextBox el)=> el.Name == "SomeTexBox" );
  7. var button = mainWindow.FindChild((Button el)=> el.Content.Equals( "クリックしてテスト" ));
  8. button.Raise(ButtonBase.ClickEvent);
  9. Assert.AreEqual( "Habrahabr" 、textBox.Get(x => x.Text));
  10. application.Invoke(x => x.Shutdown());
  11. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


2番目のボタンをクリックした後のテキスト。





  1. [テスト方法]
  2. [HostType( "Moles" )]
  3. public void TestSecondButton()
  4. {
  5. var application = UI.Run(()=> new App {MainWindow = new MainWindow()});
  6. var mainWindow = application.Get(x => x.MainWindow);
  7. var dateTimeExpect = new DateTime (2011、12、08、12、30、25);
  8. MDateTime.NowGet =()=> dateTimeExpect;
  9. var button = mainWindow.FindChild((Button el)=> el.Content.Equals( "クリックしてテスト2" ));
  10. button.Raise(ButtonBase.ClickEvent);
  11. var textBox = mainWindow.FindChilds <TextBox>().First();
  12. Assert.AreEqual(dateTimeExpect.ToString()、textBox.Get(x => x.Text));
  13. application.Invoke(x => x.Shutdown());
  14. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


ここでは、 Molesフレームワークを使用してDateTime.Nowの呼び出しをブロックしました。その概要は、たとえばここで確認できます



もちろん、アプリケーションを作成し、そこからウィンドウを取得することをテストクラスの静的コンストラクターに導入して、テストスイートの合格とコードの簡素化にかかる時間を短縮することができます。



おわりに



UIテストの自動化にどの方法を選択するかは、現在のプロジェクトの特定の現実とあなた自身の経験に基づいて決定する必要があります。 UIのテストを作成するための基本的な方法を強調してみましたが、UIの利点と欠点を試してみる機会がありました。 この記事がお役に立てば幸いです。



参照資料



.Net UIオートメーション

ホワイトプロジェクト

Visual Studio 2010コード化されたUIテスト

テスト完了

ほくろ

テスト対象アプリケーションのソース+ VisualTreeHelperを使用したサンプルテスト (日付でテストを実行するには、Moleをインストールする必要があります)




All Articles