Appiumでの画面タップ操作(ジェスチャー)

Androidテストにおいて、画面をタップする必要が生じる場面があります。

Appiumの旧バージョンでは、以下のコードを使用してAndroid画面をタップするとエラーは発生しませんでした。 Map tap = new HashMap(); tap.put("tapCount", new Double(2)); tap.put("touchCount", new Double(1)); tap.put("duration", new Double(0.5)); tap.put("x", new Double(300)); tap.put("y", new Double(300)); driver.executeScript("mobile: tap", tap); driver.executeAsyncScript(script, args);

しかし、Appiumを新バージョンにアップグレード後、このコードを使用すると以下のエラーが発生します: org.openqa.selenium.WebDriverException: Method has not yet been implemented (WARNING: The server did not provide any stacktrace information)

そのため、このメソッドは使用できなくなりました。driver.tapを使用して画面をタップすると、 driver.tap(1, 100, 100, 200); 高い確率でエラーが発生し、「An unknown server-side error occurred」というエラーメッセージが表示されます。このようなエラーは、自動化の長期運用に支障をきたします。

資料を調査したところ、Appiumの公式資料によると、以下の方法で画面をタップするとエラーが発生せず安定しています: TouchAction action = new TouchAction(driver); action.tap(100, 100).perform();

これにより、画面タップ時のエラー問題が解決します。

https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/touch-actions.md

モバイルジェスチャーの自動化

Selenium WebDriver仕様では、特定の種類のモバイル操作はサポートされていますが、そのパラメータは常に基盤となるデバイス自動化(iOSの場合はUIAutomationなど)が提供する機能に容易にマッピングできるとは限りません。この点を考慮し、Appiumは最新の仕様(https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html#multiactions-1)で定義されている新しいTouchAction / MultiAction APIを実装しています。これは、元のJSON WireプロトコルにおけるTouchAction APIの初期バージョンとは異なります。

これらのAPIを使用すると、複数のアクチュエーターを組み合わせて任意のジェスチャーを構築できます。このAPIの使用例については、お使いの言語のAppiumクライアントドキュメントを参照してください。

TouchAction / MultiAction APIの概要

TouchAction

TouchActionオブジェクトは、イベントのチェーンを保持します。

すべてのAppiumクライアントライブラリでは、タッチオブジェクトが作成され、一連のイベントが与えられます。

仕様で利用可能なイベントは以下の通りです:

  • press
  • release
  • moveTo
  • tap
  • wait
  • longPress
  • cancel
  • perform

以下は疑似コードでのアクション作成例です:

TouchAction().press(el0).moveTo(el1).release()

上記は、ユーザーが要素を押し下げ、指を別の位置にスライドし、画面から指を離す動作をシミュレートします。

Appiumはイベントを順番に実行します。ジェスチャーのタイミングを制御するためにwaitイベントを追加できます。

moveToの座標は、現在の位置に対して相対的です。例えば、100,100から200,200までドラッグするには、以下のように実現できます:

.press(100,100) // 100,100から開始
.moveTo(100,100) // XとYをそれぞれ100増やし、200,200で終了

Appiumクライアントライブラリでは、これを実装する方法が異なります。例えば、moveToイベントに座標または要素を渡すことができます。座標と要素の両方を渡すと、座標は現在の位置に対してではなく、要素の位置に対して相対的と見なされます。

performイベントを呼び出すと、一連のイベント全体がAppiumに送信され、デバイス上でタッチジェスチャーが実行されます。

Appiumクライアントでは、TouchActionオブジェクトのperformイベントを呼び出すのではなく、ドライバーオブジェクトを介して直接TouchActionを実行することもできます。

疑似コードでは、以下の2つは同等です:

TouchAction().tap(el).perform()

driver.perform(TouchAction().tap(el))

MultiTouch

MultiTouchオブジェクトは、TouchActionのコレクションです。

MultiTouchジェスチャーには、addperformの2つのメソッドしかありません。

addは、このMultiTouchに別のTouchActionを追加するために使用されます。

performが呼び出されると、MultiTouchに追加されたすべてのTouchActionがAppiumに送信され、同時に発生したかのように実行されます。Appiumは、まずすべてのTouchActionの最初のイベントを一緒に実行し、次に2番目のイベントを、というように順番に実行します。

2本の指でタップする疑似コードの例:

action0 = TouchAction().tap(el)
action1 = TouchAction().tap(el)
MultiAction().add(action0).add(action1).perform()

バグと回避策

iOS 7.0 - 8.xシミュレータには、ScrollViews、CollectionViews、TableViewsがUIAutomation(AppiumがiOSの裏側で使用する)によって開始されたジェスチャーを認識しないという不幸なバグが存在します。この問題を回避するために、これらのビューをスクロールするのに役立つ別の関数scrollへのアクセスを提供しています。

スクロール

この特別な機能へのアクセスを可能にするために、ドライバーのexecuteまたはexecuteScriptメソッドをオーバーライドし、コマンドの先頭にmobile:を接頭辞として付けます。以下に例を示します:

スクロールするには、スクロールする方向をパラメータとして渡します。

// JavaScript
driver.execute('mobile: scroll', {direction: 'down'})
// Java
JavascriptExecutor js = (JavascriptExecutor) driver;
HashMap<String, String> scrollObject = new HashMap<String, String>();
scrollObject.put("direction", "down");
js.executeScript("mobile: scroll", scrollObject);
# Ruby
execute_script 'mobile: scroll', direction: 'down'
# Python
driver.execute_script("mobile: scroll", {"direction": "down"})
// C#
Dictionary<string, string> scrollObject = new Dictionary<string, string>();
scrollObject.Add("direction", "down");
((IJavaScriptExecutor)driver).ExecuteScript("mobile: scroll", scrollObject));
$params = array(array('direction' => 'down'));
$driver->executeScript("mobile: scroll", $params);

方向と要素を使用してスクロールする例。

// JavaScript
driver.execute('mobile: scroll', {direction: 'down', element: element.value.ELEMENT});
// Java
JavascriptExecutor js = (JavascriptExecutor) driver;
HashMap<String, String> scrollObject = new HashMap<String, String>();
scrollObject.put("direction", "down");
scrollObject.put("element", ((RemoteWebElement) element).getId());
js.executeScript("mobile: scroll", scrollObject);
# Ruby
execute_script 'mobile: scroll', direction: 'down', element: element.ref
# Python
driver.execute_script("mobile: scroll", {"direction": "down", element: element.getAttribute("id")})
// C#
Dictionary<string, string> scrollObject = new Dictionary<string, string>();
scrollObject.Add("direction", "down");
scrollObject.Add("element", <element_id>);
((IJavaScriptExecutor)driver).ExecuteScript("mobile: scroll", scrollObject));
$params = array(array('direction' => 'down', 'element' => element.GetAttribute("id")));
$driver->executeScript("mobile: scroll", $params);

スライダーの自動化

iOS

// Java
// スライダーの値は0から1の間の数値の文字列表現です
// 例えば、"0.1"は10%、"1.0"は100%を意味します
WebElement slider =  driver.findElement(By.xpath("//window[1]/slider[1]"));
slider.sendKeys("0.1");

Android

Androidでスライダーを操作する最良の方法は、TouchActionsを使用することです。

タグ: appium Android automation touchactions mobile-testing

7月13日 22:19 投稿