特集:UIオートメーションによる自動UIテストの実践

WindowsアプリのUIテストを自動化しよう

クロノス 亀野 弘嗣
2008/06/03
Page1 Page2 Page3

テスト・クラスの作成

 次にサンプル・アプリケーションをテストするテスト・クラスを作成する。

 そのテスト内容は、以下である。

  1. テキストボックスへ「ねこ」を入力
  2. [追加]ボタンを押下
  3. テキストボックスへ「いぬ」を入力
  4. [追加]ボタンを押下

 検証内容は、以下である。

  1. リストボックスの1つ目のアイテムに「ねこ」が追加されている。
  2. リストボックスの2つ目のアイテムに「いぬ」が追加されている。
  3. リストボックスに追加されたアイテム数は2つである。

 そのほか、このコードにはテストを開始するためのアプリケーションの起動と、テストを終了するためのアプリケーションの終了も含んでいる。

 以下のリスト2は、テスト・クラスの全ソース・コードの内容である。

using NUnit.Framework;
using System.Windows.Automation;
using System.Threading;
using System.Diagnostics;

namespace SampleAppTest
{
  [TestFixture]
  public class Class1
  {
    private AutomationElement aeForm;

    [SetUp]
    public void SetUp()
    {
      // テスト対象のアプリケーションを起動
      Process app = Process.Start(
        @"..\..\..\SampleApp\bin\Debug\SampleApp.exe");
      Thread.Sleep(1000);

      // フォームへの参照を
      // AutomationElementオブジェクトとして取得
      aeForm = AutomationElement.FromHandle(app.MainWindowHandle);
    }

    [Test]
    public void AddTwoItem()
    {
      // 操作対象のコントロールへの参照を
      // AutomationElementオブジェクトとして取得
      AutomationElement txtInput =
        FindElement(aeForm, "txtInput");
      AutomationElement btnAdd =
        FindElement(aeForm, "btnAdd");
      AutomationElement lstResults =
        FindElement(aeForm, "lstResults");

      // 操作対象のコントロール・パターンを取得
      ValuePattern vpTxtInput =
        (ValuePattern)txtInput.GetCurrentPattern(
          ValuePattern.Pattern);
      InvokePattern ipBtnAdd =
        (InvokePattern)btnAdd.GetCurrentPattern(
          InvokePattern.Pattern);

      // 1つ目の値を入力、[追加]ボタン押下
      vpTxtInput.SetValue("ねこ");
      ipBtnAdd.Invoke();

      // 2つ目の値を入力、[追加]ボタン押下
      vpTxtInput.SetValue("いぬ");
      ipBtnAdd.Invoke();
     
      // リストボックスの値を取得するため、
      // AutomationElementCollectionオブジェクトを取得
      AutomationElementCollection aecListItems =
        lstResults.FindAll(
          TreeScope.Children,
          new PropertyCondition(
            AutomationElement.ControlTypeProperty,
            ControlType.ListItem));

      // 検証
      Assert.AreEqual("ねこ", aecListItems[0].Current.Name,
        "1つ目の追加アイテム");
      Assert.AreEqual("いぬ", aecListItems[1].Current.Name,
        "2つ目の追加アイテム");
      Assert.AreEqual(2, aecListItems.Count,
         "リストに追加されたアイテム数");
    }

    [TearDown]
    public void TearDown()
    {
      // テスト対象アプリケーションを終了
      WindowPattern wpForm =
        (WindowPattern)aeForm.GetCurrentPattern(
          WindowPattern.Pattern);
      wpForm.Close();
    }

    /// <summary>
    /// 指定されたルート要素から要求されたAutomationIDの要素を探し
    /// 最初に見つかった要素を返す
    /// </summary>
    /// <param name="rootElement">RootElement</param>
    /// <param name="automationID">AutomationId</param>
    /// <returns>AutomationElement</returns>
    private AutomationElement FindElement(AutomationElement rootElement, string automationId)
    {
      AutomationElement element =
        rootElement.FindFirst(
          TreeScope.Element | TreeScope.Descendants,
          new PropertyCondition(
            AutomationElement.AutomationIdProperty,
            automationId));

      return element;
    }
  }
}
リスト2 テスト・クラス(NUnit用)の全ソース・コードの内容
ちなみにVisual Studio 2008(Professional Edition以上)の単体テスト機能を使う場合は、冒頭で「using Microsoft.VisualStudio.TestTools.UnitTesting;」というコードで名前空間をインポートして、コード中のTestFixture属性はTestClass属性に、SetUp属性はTestInitialize属性に、Test属性はTestMethod属性に、TearDown属性はTestCleanup属性に、それぞれ置き換えればよい。

参照設定

 UIオートメーションを使用するために、UIAutomationClientとUIAutomationTypes(アセンブリ)への参照を追加する。

 なお、通常NUnitを使用して単体テストを作成する場合は、テストするアプリケーションへの参照が必要だが、今回作成するテスト・クラスは、UIオートメーションを使用してアプリケーションを操作するため、アプリケーションへの参照は不要である。

 それではリスト2のコード内容について説明していこう。

SetUpメソッド(SetUpメソッド内の処理)

 SetUpメソッドでは、テスト対象アプリケーションを起動し、そのプロセス情報からフォームへの参照(AutomationElementオブジェクト)を取得する。具体的なコードは次のようになっている。

Process app = Process.Start(
  @"..\..\..\SampleApp\bin\Debug\SampleApp.exe");
Thread.Sleep(1000);

aeForm = AutomationElement.FromHandle(app.MainWindowHandle);
リスト3 SetUpメソッド内のコード

 リスト3では、Process.Startメソッドを呼び出すことにより、テスト対象アプリケーションを起動し(テスト対象アプリケーションのパスは、実行環境に合わせて変更が必要)、そのプロセス情報(app)を取得する。次にThread.Sleepメソッドを呼び出、しアプリケーションの起動が完了するまで待機する(ここでは1秒以内で起動すると想定している)。

 プロセス情報からWindowsフォーム・アプリケーションのメイン・ウィンドウ・ハンドルを取得して、それをパラメータに指定してFromHandleメソッドを呼び出すことで、フォームへの参照であるAutomationElementオブジェクト(aeForm)が取得できる。このAutomationElementオブジェクトは、UIオートメーションの機能を実行するためのルート要素として使用する。

FindElementメソッド(FindElementメソッド内の処理)

 リスト2の一番下にあるFindElementメソッドは、対象となるコントロールへの参照(AutomationElementオブジェクト)を取得するためのもので、第1パラメータに指定されたルート要素の配下にあるツリー構造から、第2パラメータに指定されたAutomationID値の要素を探し、最初に見つかった要素を返す。具体的なコードは次のようになっている。

private AutomationElement FindElement(AutomationElement rootElement, string automationId)
{
  AutomationElement element =
    rootElement.FindFirst(
      TreeScope.Element | TreeScope.Descendants,
      new PropertyCondition(
        AutomationElement.AutomationIdProperty, automationId));

  return element;
}
リスト4 FindElementメソッドのコード内容

 FindElementメソッドの内部について説明していこう。

 ここでは特定のUI要素を見つけるために、ルート要素であるAutomationElementオブジェクトが持つFindFirstメソッドを使用している。FindFirstメソッドは、UIオートメーション・ツリーから、指定した条件と一致する最初の子要素または子孫要素を返す(ちなみに、すべての子/子孫要素を返すFindAllメソッドもある)。

 FindFirstメソッドの第1パラメータには、検索のスコープを指定する。スコープの種類(TreeScope型のフィールド)は以下のとおり。スコープはOR演算子により組み合わせて指定できる。

スコープの種類 説明
TreeScope.Element 要素自体を含む
TreeScope.Children 要素の直接の子を含む
TreeScope.Descendants 要素の子孫を含む
FindFirstメソッドの第1パラメータに指定するスコープの種類

 今回は、要素自体(TreeScope.Element)または、要素の子孫(TreeScope.Descendants)のスコープを指定する。

 FindFirstメソッドの第2パラメータには、Condition型のオブジェクトにより検索条件を指定する。Condition型のサブクラスにはさまざまな種類があるが、今回はプロパティの値を指定するPropertyConditionクラスを使用している。PropertyConditionクラスのコンストラクタには、プロパティの種類(本稿の例では「AutomationElement.AutomationIdProperty」)、プロパティの値(本稿の例では「automationId」)を指定する。

 なお、テスト対象アプリケーションのコントロールの情報を取得するには、Windows SDKに付属しているUISpyを使用すると、要素を簡単にツリー表示でき、そのプロパティなどを調べられるので便利だ。

 以上をまとめると、FindElementメソッドは、FindFirstメソッドを使用して、ルート要素(rootElement)から、AutomationIdプロパティの値がautomationIdと一致する要素を、要素自体(TreeScope.Element)または要素の子孫(TreeScope.Descendants)のスコープで探し出し、それを戻り値で返すというわけだ。

 では、いよいよ自動テストを実施するコードに移ろう。


 INDEX
  [特集] UIオートメーションによる自動UIテストの実践
  WindowsアプリのUIテストを自動化しよう
    1. UI オートメーションとは? サンプル・アプリについて
  2. テスト・クラスの作成と、コードの解説
    3. 自動テストを実施するコードと、その実施


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間