下記記事にある通り、「WinAppDriver UI Recorder」が公開されたので、さっそく試してみました。
米Microsoftは20日(現地時間)、新しいオープンソースツール「WinAppDriver UI Recorder」を「Windows Application Driver」の一部としてリリースした。自動UIテストを作成するのに役立つ。
https://forest.watch.impress.co.jp/docs/news/1128952.html より
Microsoftは2018年6月20日(米国時間)、「Windows Application Driver」(WinAppDriver)コミュニティー向けの新しいオープンソースツール「WinAppDriver UI Recorder」(以下、UI Recorder)の公開を発表した。UI Recorderは、自動化されたUI(ユーザーインタフェース)テストのスクリプトを簡単に作成できるツールだ。
http://www.atmarkit.co.jp/ait/articles/1806/22/news036.html
WinAppDriver UI Recorderの使い方
GitHubのReleasesページにZip形式で圧縮されたバイナリファイルがありますが、今回はVisual Studio Community 2017でビルドしたものを使用しました。
WinAppDriver UI Recorderを使うとUI要素のXPathクエリを生成でき、上図の通り「Record」ボタンをクリックすると、操作を記録することができます。
生成されたコード
電卓を操作した際に生成されたコードが下記になります。
// LeftClick on "One" at (75,20) Console.WriteLine("LeftClick on \"One\" at (75,20)"); string xp1 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num1Button\"][@Name=\"One\"]"; var winElem1 = MyDesktopSession.FindElementByXPath(xp1); if (winElem1 != null) { winElem1.Click(); } else { Console.WriteLine($"Failed to find element {xp1}"); return; } // LeftClick on "Two" at (44,20) Console.WriteLine("LeftClick on \"Two\" at (44,20)"); string xp2 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num2Button\"][@Name=\"Two\"]"; var winElem2 = MyDesktopSession.FindElementByXPath(xp2); if (winElem2 != null) { winElem2.Click(); } else { Console.WriteLine($"Failed to find element {xp2}"); return; } // LeftClick on "Three" at (36,20) Console.WriteLine("LeftClick on \"Three\" at (36,20)"); string xp3 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num3Button\"][@Name=\"Three\"]"; var winElem3 = MyDesktopSession.FindElementByXPath(xp3); if (winElem3 != null) { winElem3.Click(); } else { Console.WriteLine($"Failed to find element {xp3}"); return; } // LeftClick on "Plus" at (19,18) Console.WriteLine("LeftClick on \"Plus\" at (19,18)"); string xp4 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"StandardOperators\"][@Name=\"Standard operators\"]/Button[@AutomationId=\"plusButton\"][@Name=\"Plus\"]"; var winElem4 = MyDesktopSession.FindElementByXPath(xp4); if (winElem4 != null) { winElem4.Click(); } else { Console.WriteLine($"Failed to find element {xp4}"); return; } // LeftClick on "Four" at (57,18) Console.WriteLine("LeftClick on \"Four\" at (57,18)"); string xp5 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num4Button\"][@Name=\"Four\"]"; var winElem5 = MyDesktopSession.FindElementByXPath(xp5); if (winElem5 != null) { winElem5.Click(); } else { Console.WriteLine($"Failed to find element {xp5}"); return; } // LeftClick on "Five" at (80,19) Console.WriteLine("LeftClick on \"Five\" at (80,19)"); string xp6 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num5Button\"][@Name=\"Five\"]"; var winElem6 = MyDesktopSession.FindElementByXPath(xp6); if (winElem6 != null) { winElem6.Click(); } else { Console.WriteLine($"Failed to find element {xp6}"); return; } // LeftClick on "Six" at (34,18) Console.WriteLine("LeftClick on \"Six\" at (34,18)"); string xp7 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num6Button\"][@Name=\"Six\"]"; var winElem7 = MyDesktopSession.FindElementByXPath(xp7); if (winElem7 != null) { winElem7.Click(); } else { Console.WriteLine($"Failed to find element {xp7}"); return; } // LeftClick on "Equals" at (59,29) Console.WriteLine("LeftClick on \"Equals\" at (59,29)"); string xp8 = "/Pane[@Name=\"デスクトップ 1\"][@ClassName=\"#32769\"]/Window[@Name=\"Calculator - Calculator\"][@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"StandardOperators\"][@Name=\"Standard operators\"]/Button[@AutomationId=\"equalButton\"][@Name=\"Equals\"]"; var winElem8 = MyDesktopSession.FindElementByXPath(xp8); if (winElem8 != null) { winElem8.Click(); } else { Console.WriteLine($"Failed to find element {xp8}"); return; }
これをこのままコピー&ペーストしても動作しないので、一部コードを書き足して実行したところ、要素の取得で躓きました。
仕方が無いので、XPathの一部を編集したのが下記のコードです。
(「Windows Application Driverを試してみました。」記事に書いている通り、コードを実行する際はNuGetで「Appium.WebDriver」を追加してください。)
using System; using OpenQA.Selenium.Appium.Windows; using OpenQA.Selenium.Remote; namespace WinAppDriverTest { class Program { private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723"; private const string CalculatorAppId = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"; protected static WindowsDriver<WindowsElement> session; static void Main(string[] args) { string serverPath = System.IO.Path.Combine( System.Environment.GetFolderPath( System.Environment.SpecialFolder.ProgramFilesX86 ), @"Windows Application Driver", "WinAppDriver.exe" ); System.Diagnostics.Process.Start(serverPath); DesiredCapabilities appCapabilities = new DesiredCapabilities(); appCapabilities.SetCapability("app", CalculatorAppId); session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities); session.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1.5)); // LeftClick on "One" at (75,20) Console.WriteLine("LeftClick on \"One\" at (75,20)"); string xp1 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num1Button\"][@Name=\"One\"]"; var winElem1 = session.FindElementByXPath(xp1); if (winElem1 != null) { winElem1.Click(); } else { Console.WriteLine($"Failed to find element {xp1}"); return; } // LeftClick on "Two" at (44,20) Console.WriteLine("LeftClick on \"Two\" at (44,20)"); string xp2 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num2Button\"][@Name=\"Two\"]"; var winElem2 = session.FindElementByXPath(xp2); if (winElem2 != null) { winElem2.Click(); } else { Console.WriteLine($"Failed to find element {xp2}"); return; } // LeftClick on "Three" at (36,20) Console.WriteLine("LeftClick on \"Three\" at (36,20)"); string xp3 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num3Button\"][@Name=\"Three\"]"; var winElem3 = session.FindElementByXPath(xp3); if (winElem3 != null) { winElem3.Click(); } else { Console.WriteLine($"Failed to find element {xp3}"); return; } // LeftClick on "Plus" at (19,18) Console.WriteLine("LeftClick on \"Plus\" at (19,18)"); string xp4 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"StandardOperators\"][@Name=\"Standard operators\"]/Button[@AutomationId=\"plusButton\"][@Name=\"Plus\"]"; var winElem4 = session.FindElementByXPath(xp4); if (winElem4 != null) { winElem4.Click(); } else { Console.WriteLine($"Failed to find element {xp4}"); return; } // LeftClick on "Four" at (57,18) Console.WriteLine("LeftClick on \"Four\" at (57,18)"); string xp5 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num4Button\"][@Name=\"Four\"]"; var winElem5 = session.FindElementByXPath(xp5); if (winElem5 != null) { winElem5.Click(); } else { Console.WriteLine($"Failed to find element {xp5}"); return; } // LeftClick on "Five" at (80,19) Console.WriteLine("LeftClick on \"Five\" at (80,19)"); string xp6 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num5Button\"][@Name=\"Five\"]"; var winElem6 = session.FindElementByXPath(xp6); if (winElem6 != null) { winElem6.Click(); } else { Console.WriteLine($"Failed to find element {xp6}"); return; } // LeftClick on "Six" at (34,18) Console.WriteLine("LeftClick on \"Six\" at (34,18)"); string xp7 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"NumberPad\"][@Name=\"Number pad\"]/Button[@AutomationId=\"num6Button\"][@Name=\"Six\"]"; var winElem7 = session.FindElementByXPath(xp7); if (winElem7 != null) { winElem7.Click(); } else { Console.WriteLine($"Failed to find element {xp7}"); return; } // LeftClick on "Equals" at (59,29) Console.WriteLine("LeftClick on \"Equals\" at (59,29)"); string xp8 = "//*[@ClassName=\"ApplicationFrameWindow\"]/Window[@Name=\"Calculator\"][@ClassName=\"Windows.UI.Core.CoreWindow\"]/Group[@ClassName=\"LandmarkTarget\"]/Group[@AutomationId=\"StandardOperators\"][@Name=\"Standard operators\"]/Button[@AutomationId=\"equalButton\"][@Name=\"Equals\"]"; var winElem8 = session.FindElementByXPath(xp8); if (winElem8 != null) { winElem8.Click(); } else { Console.WriteLine($"Failed to find element {xp8}"); return; } /* if (session != null) { session.Quit(); session = null; } */ Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } } }
実行画面
下図の通り、WinAppDriverを使って問題なく電卓の操作ができました。
感想
軽く触ってみた感じ、要素のXPathを取得できる点は便利でしたが、生成されるコードは冗長で、Excelのマクロ記録機能のような印象を受けました。
UI要素を調べるツールとして、現時点(2018年6月)ではInspectの代わりになるものではなく、Inspectと併用することで、WinAppDriverを使ったコードが書きやすくなるツールなのだと思います。
まだ、出始めのツールなので、今後に期待ですね!
この記事へのコメントはありません。