Developer's Blog

iOS SDK 5 で UIAutomation が良くなってたという話

Mac / iOS 開発担当の松本です。

以前同じチームのミヤモトさんが紹介記事を書いていた UIAutomation ですが、使いにくくて使っていませんでした。というのも以前のバージョンはコマンドラインからテストを実行できず、毎回 Instruments を手で起動して、、という残念な状態だったからです。

ところが iOS SDK 5からはコマンドラインからテストを実行できるようになったと言うので再度トライしてみました。

コマンドラインからテストの実行

手始めに下記のハローワールドをコマンドラインから実行してみます。

 // hello_test.js UIALogger.logMessage("Hello, UI Automation!"); 

テストの実行は instruments コマンドで行います。

 $ instruments \ -t /Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate \ "/Users/matsumoto/Library/Application Support/iPhone Simulator/5.0/Applications/140BBB5F-9E4E-439C-9B14-AA2A6BF2F93F/Sleipnir.app/Sleipnir" \ -e UIASCRIPT /path/to/hello_test.js 

アプリケーションや、tracetemplate ファイルへのパスを記述する必要があるので複雑な感じですね。Python スクリプトでラップしておきます。

 #!/usr/bin/env python # -*- coding: utf-8 -*- import sys import os def main(app, testfile): def get_app_path(app): home = os.environ['HOME'] app_dir= '%(home)s/Library/Application Support/iPhone Simulator/5.0/Applications/' % locals() for (root, dirs, files) in os.walk(app_dir): if root != app_dir: continue for directory in dirs: path = '%(root)s%(directory)s/%(app)s.app' % locals() if os.path.exists(path): return path return None if not os.path.exists(testfile): sys.exit('%s does not exists' % (testfile,)) app_path = get_app_path(app) if not app_path: sys.exit('%s does not exists' % (app,)) trace = '/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate' cmd = 'instruments -t %(trace)s "%(app_path)s" -e UIASCRIPT %(testfile)s' % locals() return os.system(cmd) if __name__ == '__main__': if len(sys.argv) != 3: sys.exit('Usage: runtest.py [app name] [test file path]') app = sys.argv[1] testfile = sys.argv[2] exit(main(app, testfile)) 

これで例えば Sleipnir に対してテストを実行する時は、以下のようにします。シンプルになりましたね。

 $ runtest.py Sleipnir hello_test.js 

tuneup_js の導入

さてテストスクリプトを無事にコマンドラインから実行できた訳ですが、肝心のテストを書く環境がまだ整っていません。デフォルトではアサートすら無いので、テストコードを書くハードルが高いです。そこで tuneup_js を使いましょう。tuneup_js は UIAutomation 用の軽いテストライブラリで、下記のようなテストを書けるようになります。

 #import "tuneup/tuneup.js" test("my test", function(target, app) { mainWindow = app.mainWindow(); navBar = mainWindow.navigationBar(); leftButton = navBar.leftButton(); rightButton = navBar.rightButton(); assertEquals("Back", leftButton.name()); assertEquals("Done", rightButton.name()); }); 

導入はソースコードをダウンロードして、テストスクリプトから #import を呼ぶだけで OK です。tuneup_js のおかげで馴染みのあるテストの雰囲気がでてきました。これでようやく勇気がわいてきましたね。

実際のテストコードと注意点

tuneup_js を使って実際にテストコードを書いてみました。以下は Sleipnir Mobile で以前発生した、ブックマーク画面が閉じれなくなる問題を確認するためのテストコードになります。

 #import "tuneup/tuneup.js" UIATarget.localTarget().delay(1.0); test("Open Bookmark View", function(target, app) { var window = app.mainWindow(); // browser view => bookmark view var browser_view = window.elements().withName('BrowserView')[0]; assertNotNull(browser_view, "Failed to get BrowserView"); var bookmark_button = browser_view.toolbar().buttons()["ToolBookmark"]; assertNotNull(bookmark_button, "Failed to get ToolBookmark"); bookmark_button.tap(); var bookmark_view = window.elements().withName("BookmarkView")[0]; assertNotNull(bookmark_view, "Failed to get BookmarkView"); // bookmark edit => new folder => cancel var edit_button = window.toolbar().buttons()["編集"]; assertNotNull(edit_button, "Failed to get Bookmark Edit Button"); edit_button.tap(); var newfolder_button = window.toolbar().buttons()["新規フォルダ"]; assertNotNull(newfolder_button, "Failed to get New Folder Button"); newfolder_button.tap(); var cancel_button = window.navigationBar().buttons()["キャンセル"]; assertNotNull(cancel_button, "Failed to get Cancel Button"); cancel_button.tap(); }); 

なお上記スクリプト内で “BrowserView”、”BookmarkView” を Window オブジェクトから名前を指定して取得していますが、その為には少しだけ準備が必要です。取得したい view の accessibilityIdentifier プロパティに Objective-C のコードで名前を設定しておきましょう。例えば View をホストしている ViewController の viewDidLoad 等で以下のように指定します。

self.view.accessibilityIdentifier = @"BookmarkView";

これで名前で View にアクセスできるのでテストを簡潔に記述できます。

コマンドラインから使えるようになった UIAutomation に、tuneup_js を合わせて使うと、割と簡単に UI テストが記述できるという事がわかりました。ずっとリグレッションテストを導入したいと思っていたので、上手く活用してより効率的なテストをしていきたいですね。

Copyright © 2019 Fenrir Inc. All rights reserved.