モンキーランナー。 Androidでのピクセル完璧なWebページのテスト

Googleが自動化テストmonkeyrunnerをテストするためのツールをリリースしてから多くの時間が経過しましたが、改善は見られません。 ただし、Webページを定期的にチェックして最適なツールの正しいレイアウトを確認するタスクは見つかりませんでした。 Android上のページのスクリーンショットをスクロールサポートと比較するための既製のスクリプトが必要な場合は、 リンクからすぐにダウンロードできます。 カットの下で、マネキンが抱えている問題とその克服方法が通知されます。



挑戦する


簡単に言えば、タスクは次のとおりです。リストのページを調べて、それらが以前と同じ(またはほぼ同じ)に見えることを確認します。 もちろん、長いページを最後までスクロールする必要があります。



決定アルゴリズム


それ自体では、非常に単純に見えます:

  1. リストからページを開く
  2. スクリーンショットを撮り、リファレンスと比較します
  3. スクリーンショットがそれほど変わらない場合は、ページを1画面スクロールして、ステップ2を繰り返します
  4. ページが終了した場合、または結果が元のページと大きく異なる場合は、次のページに進みます


monkeyrunnerにはスクリーンショットを撮るための既製のtakeSnapshot関数があり、その比較のために、100%一致するだけでなく、画像がどれだけ似ているかを評価できる非常に素晴らしいImageMagickがあります。 ImageMagicには多くの比較メトリックがあります。-metricRMSEを使用します。

ページのスクロールを終了する時期を判断するには、最後の2つの画面を比較し、一致する場合は最後に到達します。

Python-junit-xmlは 、結果をCIまたはお気に入りのIDEにエクスポートするために使用されます



落とし穴


ここで最も興味深いのは、このすべてを「真正面から」行うと、何も機能しないことです。 いくつかの理由があります。



1. Windows

monkeyrunnerには興味深い「機能」があります。Windowsの実行中にmonkeyrunner.batは、現在のディレクトリをマネキンが置かれているディレクトリに変更します。 その結果、スクリプト内のすべての相対パスとインポートディレクティブが機能しなくなります

この動作を克服するには、スクリプト自体がその場所を特定し、絶対パスでのみ動作し続ける必要があります。 これは次のように行われます。

filepath = path.split(os.path.realpath(__file__))[0] BASE_PATH = path.split(filepath)[0].encode(FILENAMES_ENCODING) try: import config from junit_xmls import TestSuite, TestCase except ImportError: #dirty hack that loads 3rd party modules from script's dir not from working dir, which is always changed by windows monkeyrynner import imp config = imp.load_source('config', BASE_PATH+'/src/config.py') junit_xml = imp.load_source('junit_xml', BASE_PATH+'/src/junit_xml/__init__.py') TestSuite = junit_xml.TestSuite TestCase = junit_xml.TestCase
      
      







2.ロシア文字

ロシアのウィキペディアをテストする最初の試みは失敗しました。マネキンは2番目のpythonに基づいており、ユニコードと国のシンボルの既知の問題をすべて継承しているためです。 可能な限りエンコーディングを明示的に指定し、junit_xmlをわずかに変更する必要がありました。その作成者は国別文字を認識していませんでした。

たとえば、ファイルを正しく作成するには、名前を明示的にUnicodeに変換する必要があります
 filePath.decode("utf-8")
      
      





さらに、ロシア語版のWindowsでは、ImageMagickへの間違ったパスが指定されるとスクリプトがクラッシュします。これは、 Popenがエラーメッセージでロシア語の文字を受け取ることを期待せず、Windowsがメッセージをローカライズするためです。



3.スクロール

MonkeyDevice.drag()を使用して、同じデバイスの同じブラウザーで同じページを10回スクロールすると 10の異なる結果が得られます。 ドラッグは、単に同じ方法でページをスクロールすることを保証するものではありません。 この問題を解決するために、次のトリックを適用する必要がありました:新しいページを古いページと比較し、ページの上下から数ピクセルを切り取り、元の場所を探します(残念ながらImageMagickはこれを行うこともできます)、ページがどれだけ低いか高いか位置、およびスクロールのエラーがある場合、次のスクロールでそれを減算する必要があります。 このようなフィードバックにより、スクリプトは3回目の反復で落ちず、ページの最後まで安全に実行されます。



4.メモリ消費

10〜20ページを連続して開くと、ブラウザは10〜20個のタブを作成するだけで、時間が経つとすべてのメモリが消費され、ブラウザはコマンドへの応答を停止します。 最良の場合、ページの読み込みは遅くなりますが、通常これによりmonkeyrunnerは単純にタイムアウトします。 これを回避するために、私は小さなハックに行きました。新しいページを開く前に、現在のブラウザプロセスはadbを介して単に殺されます。
 device.shell('am force-stop ' + BROWSER_PACKAGE_NAME)
      
      





このハックは、エミュレーターの標準AOSPブラウザーで大いに役立ちますが、Chrome、Opera、FFには実際には影響しないため、最終的にはWebビューでラッパーを作成する必要がありました。実際、タブのない軽量ブラウザーです。 途中で、他の2つの問題が解決されました。自己記述ブラウザはユーザーの場所へのアクセスの確認を求めず、スクリーンショットを台無しにしません。さらに、スクリプトに含めることができ、 MonkeyDevice.installPackageを通じて自動的にインストールされます。



5.接続の失敗

スクリプトをシャットダウンしてすぐに再起動すると、 MonkeyRunner.waitForConnection()がエラーで失敗する可能性が高く、通常、繰り返し呼び出しは問題なく通過します。 したがって、スクリプトの最終バージョンは常にデバイスへの接続を2回試行します。



6.画面ロック

起動時にデバイスがブロックされた場合、スクリプトは単に正しく動作しません。 これは、ハードウェアメニューボタンを押すことをシミュレートすることで回避できます(何らかの理由で、Androidデバイスのロックが解除されます)

 MonkeyDevice.press("KEYCODE_MENU", MonkeyDevice.DOWN_AND_UP)
      
      





ただし、ロックされていないデバイスではメニューが表示されるため、最初にデバイスがロックされていることを確認することをお勧めします。これは、dumpsysを使用して実行できます。

  lockScreenRegexp = re.compile('mShowingLockscreen=(true|false)') result = lockScreenRegexp.search(device.shell('dumpsys window policy')) if result: return (result.group(1) == 'true') raise RuntimeError("Couldn't determine screen lock state")
      
      







単独でロック解除しても「スリープ」デバイスの画面はオンにならないため、これらすべての操作の前に、 MonkeyRunner.wake()を強制的に呼び出す必要があります。

デバイスが正常にロック解除された後、他の機能が正しく動作し始めるという事実に頼ることができます。



合計


すべての編集で、5分ごとにクラッシュを恐れることなくページ表示の正確さをすでに確認できますが、最良の結果を得るには、テストブラウザーまたはバニラAndroidからAOSPブラウザーを選択する必要があります。



追伸 コードはビットバケツにあり、コピーおよび変更のために開いています。



All Articles