docker-android + AppImage版Appium DesktopでAndroidテスト自動化の環境をさっくり作る
うっすいネタ、いわゆる「使ってみました」ネタで恐縮です。あと、タグSeleniumってつけてますが兄弟?プロジェクトのAppiumネタです。
私の本業はいちおうソフトウェア自動テストアーキテクト*1 なわけです。主にSeleniumを使ったWebの自動テストが得意領域ってことになってるんですが、その周辺領域であるAppiumを用いたモバイルの自動テストはあんまり手を動かした経験がなく、教科書レベルのことは言えるけど……ってのがコンプレックスでした。
最近いろいろあって残業することなく早く帰るようにしてるので、自由になる時間をYouTubeの動画*2 観て溶かしてないで、ちったー手を動かしてみますかーってことで、マイパソコンでAppiumのテスト自動化開発環境を作ろうと思い立ちました。仕事ではJava + Selenide*3 なんですけど、まあ個人の遊びなので違う言語ってことでRubyで。
ご存知の通り?私は個人ではUbuntuしか使ってないので、Ubuntu上で環境を作りたい。なのですが、AppiumってNodeアプリなので、Nodeを入れなきゃなのですが、NodeもまたこれがLinuxディストリビューションのパッケージングポリシーと合わないことおびただしい*4 のでコンテナに閉じ込めたりしたい。さらにいうとAndroid SDKとかの管理もめんどくさい。なんかいいのないかなーとググっていたら。素晴らしいものがありましたよ。
AndroidのAVDとAppiumを封じ込めたコンテナ。docker run
一発で全部入り環境が立ち上がる。AVDの操作ははVNCとか入れずにブラウザーで確認可能。動画記録機能まである。これは最高なのでは……。
ということで使ってみました。
Ubuntuのバージョンは18.04 LTS(bionic beaver)です。
docker-android使ってみよう
むっちゃ簡単です。
docker run --privileged -d -p 6080:6080 -p 5554:5554 -p 5555:5555 -p 4723:4723 -e DEVICE="Samsung Galaxy S6" -e APPIUM=true --name android-container butomo1989/docker-android-x86-8.1
-e APPIUM=true
でAppiumサーバーも一緒にあげてます。
ちょっとコンテナサイズでかくて、初回実行時にはpullにしばらく待たされました*5 けど、あっさり動いた。ちょっと感動。
AppImage版Appium Desktopからつなぐ
Appiumサーバーちゃんと動いてること確認したいですね。
そのためには(もちろん、今後使うからでもありますけど)Appiumの開発ツールであるAppium Desktopも使いたい。しかしこいつも依存関係汚すのはやだなー。そう思いつつ公式の配布先↓ 見に行ったら。
あるじゃん。AppImage版。
AppImageについては深入りしませんが*6、よーはファイル取ってきて実行権限つけるだけで、インストール不要で使えるパッケージ。これなら環境汚さないしもう使わないと思ったらファイル消すだけでOKです。
早速取ってきて(依存関係全部抱いてる関係でちょっとでっかいです)、実行権限つけて起動。
今回はdocker-android内でAppiumサーバー動いてるので「Start Server」は押さずに、メニューから「File」>「New Session Window」を起動。セッション画面を起動します。
で、Desired Capabilitesを以下のようにして*7、単に設定画面を開いてみます。
{ "platformName": "android", "deviceName": "device", "appActivity": "Settings", "appPackage": "com.android.settings", "takesScreenshot": false }
Start Session! うりゃ!
ちゃんと動いてる! やったね。
スクリプトから起動してみる
ではRubyのスクリプトから起動してみましょうか。せっかくなので今度はアプリケーションをサイドロードして起動して、それを操作してみたいですね。
っと、その前に、apkを導入するときには、apkのあるフォルダを /root/tmp
にマウントしろとドキュメントに書いてあるので、動いてるコンテナを止めて再起動します。スクリプトを作るフォルダにapkは置くことにして、雑に $PWD
をマウントします。
docker stop android-container docker rm android-container docker run --privileged -d -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -e DEVICE="Samsung Galaxy S6" -e APPIUM=true -e CONNECT_TO_GRID=true -e APPIUM_HOST="127.0.0.1" -e APPIUM_PORT=4723 -e SELENIUM_HOST="172.17.0.1" -e SELENIUM_PORT=4444 -e MOBILE_WEB_TEST=true -v $PWD:/root/tmp --name android-container butomo1989/docker-android-x86-8.1
あれ、コマンド履歴見直すと CONNECT_TO_GRID
*8 はいらんのでは……。まあいいや。
アプリは、わたくし普段からAndroidアプリ開発してるわけではないので*9 手持ちのapkはなく、ので、Appiumのサンプル からApiDemos-debug.apkを取ってきて手元のディレクトリに置きました。
require 'test/unit' require 'appium_lib' class SimpleTest < Test::Unit::TestCase def setup desired_caps = { caps: { platformName: 'android', deviceName: 'device', app: '/root/tmp/ApiDemos-debug.apk', appActivity: '.ApiDemos', appPackage: 'io.appium.android.apis', takesScreenshot: false, }, appium_lib: { server_url: 'http://localhost:4723/wd/hub' } } driver = Appium::Driver.new(desired_caps, true) Appium.promote_appium_methods self.class driver.start_driver.manage.timeouts.implicit_wait = 20 end def teardown driver_quit end def test_sample puts "hello" end end
これでさくっと動いてしまいました。いやーすばらしい。
今のところテスト本体は空っぽなので、単にアプリ起動できたところまでしか確認できてませんけど。
これから、テストちょろちょろ書いていこうと思います。今回は 'test/unit' 使ってるけど、このテストフレームワークがいいよ! という推薦があれば教えていただきたいです。かなり昔に教えてもらったTurnipが気になってますが……。
まあともかく、docker-androidおすすめです。あとLinux使うならAppImage版Appium Desktopもチョー推薦です*10。
以下おまけあり。読みたい方は「続きを読む」をクリックしてください。
*1:自動テストがprofessionであって、自動がつかないテストについては不勉強の至りです……。
*2:高田馬場ゲーセンミカドの動画がお気に入りです。
*3:Selenideいいですよねえ。機能も実装も素敵。もしJavaで素のWebDriver使って苦労してるならぜひ試してみましょう。
*4:言語系パッケージシステムのライフタイムと、Linuxディストリビューションのそれとは大きく違うのでそれはしょうがないですよね。
*5:というか、待ちきれなくて寝てしまいました。うちはあんまりネットワーク太くないので……。
*7:AppiumのRubyサンプルコード から拝借。
*8:こいつの意味は続きがあればそこで書くかも。さしあたりは公式読んでください。
*9:この活動の一環として将来は作ってみたいなとは思ってますけど……いつになるかは不明。
*10:というか一般にAppImageは便利。LibreOfficeとかでも。
【執筆報告】うぶんちゅ!まがじん ざっぱ〜ん♪ vol.8(ゲストページ)
ちょっと出遅れてしまいましたが、うぶまが*1の元執筆陣など豪華なメンツが書かれている同人誌「うぶんちゅ! まがじん ざっぱ〜ん♪」の最新号、vol.8のゲストページに記事を書かせてもらいました。
私が書いた内容は「国際イベントの招致を手伝ってみましたよ」ということで、openSUSE.Asia Summit 2017 Tokyoの舞台裏的な話。数百人規模の人員が集まってスポンサーがたくさんつく言語系カンファレンスについてだとけっこう巷に情報ありますけど、完全手弁当で小さいけど国際イベントってノウハウそんなに表に出てない気がして、そういうのまとめる場を提供してくださったTeam ZPNのいくやさんほか皆様には感謝感謝です。
私のことはおいておいても、よかぜさんのバイオニックで生物で白衣なビーパーおねえさんの表紙も素敵ですし、面白い記事が満載で、目次を引き写すと:
- 表紙: よかぜ
- Ubuntuではじめる楽しいゼミ運営: おしえたかし
- ポメラDM200にUbuntuをインストールする: 柴田充也
- Boomagaを使ってPDFを小冊子印刷する方法: あわしろいくや
- NanoPi NEOで作成するテレビ視聴環境: ryunuda
- いつでも始められるmpv: kazken3
- らくごうさんちのノートPC事情: Rakugou
- Ubuntuで心理学実験: はにゅう
- 国際イベントの招致を手伝ってみましたよ(ゲストページ): おがさわらなるひこ
- 技術書典4で冊子版『ざっクリわかる Ubuntu 18.04 LTS 』を頒布できなかった顛末(ゲストページ): いくや
個人的な趣味嗜好としては、おしえさんのゼミ運営の話(Mattermostでゼミ内コミュニケーション、いいですね)、いくやさんのBoomagaネタが特に面白かったです。もひとついくやさんのゲストページは涙涙の内容でございました。はにゅうさんの心理学実験の記事も、自分とはちょっと違う世界の話でよかったです*2。
捨て記事なしの内容でこれが700円は安いと思うんだな。ということで、みなさまぜひに。購入サイト:Gumroad BOOTH
【執筆報告】日経Linux 2018年7月号 & 日経xTECH LibreOffice Kaigiレポート
たまには執筆報告など。報告がたまというわけじゃなくて、執筆のお話がそんなにあるわけじゃない(手持ちのコマ少ないですからね、わたくし……)ってことです。
日経Linux 2018年7月号にちろっと1ページ、LibreOffice 6.0のレポート記事を書きました。
何度も何度も何度も同じこと書きますがLibreOffice 3系とか4系とか5系とか6系ってバージョンはなくて、6.0でメジャーバージョンなんですよね。で、なんで5.4から6.0になったの?というと、はっきりとブランディングですよと断言してしまいました。LOOLとかもそうだししばらくご無沙汰なモバイル版とかもそうだけど、これからいろんな取り組みしてくからよろしくってことですね。
で、超豪華付録「Ubuntu 18.04 LTS 日本語 Remix 使い方が全部分かる本」にも、ありがたーーーーいことにお声がけいただきまして、ドライバーレス印刷についての記事を加筆修正して再録していただきました。前に書いたときに書きすぎて盛大にボツになった内容も、これを機に復活することができました。
記事にも書いたとおり18.04 LTSからドライバーレス印刷で自動生成されるPPDが言語設定を反映するようになったのですが、これが盛大にバグっており、バグレポしたんですけど見事にスルーされておるな……ふむ。Upstreamにレポートしたほうがいいのかなあ。
私の記事はともかくめちゃくちゃ力の入った別冊なのでUbuntuユーザーは買って損はないです。Ubuntuユーザーじゃない人も買いましょう。
ついでというわけではないですが、先日行われましたLibreOffice Kaigi 2018(ご参加いただいた皆様、ありがとうございます!)のレポートを日経xTECHさんに掲載いただきました。FacebookとかTwitterでは告知しましたけど。
よかったら読んでくださいませ。あなたのクリックが、次のレポート掲載の助けになるのです。はい。
BrowserMob Proxyを用いて認証プロキシありの環境下でSeleniumによるChrome自動操作を行う
みなさん、認証プロキシはお好きですか?
私はもちろん嫌いです。が、まあそういう環境がまだまだ多数あるということも知っておりますし諸事情を考えるとやむを得ないこともあるのかなーとは思います。
そういうところでSeleniumによるGUI自動操作を行いたいというニーズがありまして……Google先生に聞いてみるといくつか答えが得られるのです。が、仕様変更があったり条件がちょっと違ったりとかしてうまくいかない。しばらく煮詰まっていたのですが、うまくいったのでご紹介。
結論としては:
を使ってこんなコードで良さそうです*1。事前に次のような変数は初期化されているものとします。
変数名 | 意味 |
---|---|
proxyServer |
プロキシのサーバー名 |
proxyPort |
プロキシのポート番号 |
username |
プロキシ認証ユーザー名 |
password |
プロキシ認証パスワード |
// BrowserMob Proxyにて、認証プロキシにチェーンするプロキシを作成する // 参考: // https://github.com/lightbody/browsermob-proxy/blob/master/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/ChainedProxyAuthTest.groovy BrowserMobProxy bmp = new BrowserMobProxyServer(); // Don't use InetSocketAddress.unresolved(), use new InetSocketAddress instead bmp.setChainedProxy(new InetSocketAddress(proxyServer, proxyPort)); bmp.chainedProxyAuthorization(username, password, AuthType.BASIC); bmp.setTrustAllServers(true); bmp.start(); // 動作しているプロキシをSeleniumのProxyクラスに変換して登録 Proxy proxy = ClientUtil.createSeleniumProxy(bmp); proxy.setNoProxy("localhost"); // Proxy除外設定もかける DesiredCapabilities cap = new DesiredCapabilities(); cap.setCapability(CapabilityType.PROXY, proxy); System.setProperty("webdriver.chrome.driver", "./driver/linux/chromedriver"); WebDriver driver = new ChromeDriver(cap); // 古いスタイルだというのは知っております……。 driver.get("http://www.google.com"); // サンプル // ここにいろんな操作を書く driver.quit(); bmp.stop();
探し方が良くないのかもしれないのですがBrowserMob ProxyのEmbeddedモード(Javaのコード内でライブラリとして利用する方法)がなんかいい情報がなくて、あーでもないこーでもないと試行錯誤していたのですが、コード片上にコメントで書いてあるとおり ChainedProxyAuthTest.groovy
っていうズバリのテストコードがあって、それを参考にしました。
BrowserMobProxy.setChainedProxy()
に渡すための InetSocketAddress
をどうやって生成するかについては、BrowserMob Proxyの過去のIssueに答えがありました*2。
枝葉ですが、私自身はSeleniumのラッパークラスであるSelenideを使ってるので、このときのプロキシ設定は DesiredCapabilities
を使う代わりに WebDriverRunner.setProxy()
すればうまくいきます。
試行錯誤の内容
- 「自動テストスクリプトからは認証なしプロキシとして見えて、実際の認証プロキシにチェーンしてそちらに認証情報を渡す」小さなプロキシ(以下チェーンプロキシ)を立てるって方法が複数ヒットする。たしかにうまく行くのはわかるけど、いろいろあってすごくやりにくいのでこの方式は「どうしようもない場合の切り札」として検討後回し。
- Seleniumの
Proxy.setSocksUsername()
/Proxy.setSocksPassword()
を使えるって情報が複数ヒットするも、正しく動作せず。まあSocksって名前だしねえ……。もしかしたらプロキシ認証がBasic認証じゃない場合は通ったりするのかしら。 - Chromeには
--proxy-auth
ってコマンドライン引数がある……という情報があったが、指定しても無視されるし、頼りのList of Chromium Command Line Switches にはそんなオプションはない。廃止された? - 環境変数
http_proxy
で指定すれば……という意見もあったけど、今回、操作したい対象そのものはプロキシの中にあって、そこから呼ばれてるJSとかCSSが外部……という状態なので除外設定が必要。環境変数だとそういう制御ができない気がして、そっち側の調査はあまり深堀してない。 - 手詰まりなのでSeleniumHQのSlackで聞いてみたところ、……Seleniumはそういう機能ないのでBrowserMob Proxyを使うといいよと助言をもらう。
- ふむふむと調べると……。テンパっていた中で超斜め読みして、うーんこれは結局チェーンプロキシを立てる話なのでは、と誤読して、ちゃんと調べるのは後回しにした。
- 色々忙しくしてたら本件納期が差し迫っているのに解決のめど立ってなくてやばい。そんなわけでナナメ読みしたドキュメントを少し丁寧に読んだら、BrowserMob Proxyにはプロセスとして動作するモードとJava埋め込みできるモードがあって、後者ならプロセス別立て要らないのかも? しかも「Using with Selenium」なんて項目もあるし。ちょっと期待。
- ざくっと調査すると次の記事がヒット。この記事のコード例は今のBrowserMob Proxyだとまったく通らない*3けど、やりたいことはできるようなので真面目に書き方を調べる。
- ドキュメントを読んでもいまいち使い方わかんないしGoogleで探してもずばりのページはないなあ……というかJavaDocとかはないのか、と思って調べたけど、考えてみればソース読めばいいんだよね。ということで読む。
- というかソース読んでるぐらいならテスト見れば使い方わかるんじゃね? と思って、
src/test
以下を探したら正解! - 真面目に調べ始めてからコードをいじってあーでもないこーでもないってしてた時間はそんなには長くないかな。2〜3時間? 返す返すもテンパってたときにドキュメントをちゃんと読まなかったのが悔しい。
- コード書くよりめんどくさかった作業は、検証用に手元に認証プロキシがある環境を作ることでした。結局 GitHub - sameersbn/docker-squid: Dockerfile to create a Docker container image for Squid proxy server こいつを使って、
/etc/squid3/squid.conf
はホストにマウントして認証設定書いて、/etc/squid3/.htpasswd
はめんどくさかったのでコンテナ内部に直接作っちゃいましたが、これもホストにマウントすればよかったんだな。この話も気が向いたら書きます。
- コード書くよりめんどくさかった作業は、検証用に手元に認証プロキシがある環境を作ることでした。結局 GitHub - sameersbn/docker-squid: Dockerfile to create a Docker container image for Squid proxy server こいつを使って、
あとから考えるとBrowserMob ProxyをSeleniumから使うことについては:
定番のこの本に書いてあるので、もっと早く気づいてもよかった。BrowserMob Proxyはパフォーマンス測定とかにも使うやつなので、あんまり印象に残ってなかったんですよね(いいわけ)。
(メモ)Jenkins/GitLabをお試しする環境をDockerで構築する
最近、JenkinsとGitLabの連携機能とか、Jenkins Blue Oceanを試したりしています*1。 で、検証環境を手元にほしいのですが、GitLabの構築めんどくさそうだったので、Dockerだったら楽なんじゃないかなという*2。 docker-composeとかを使うところなのかもしれないですが、素朴なので手でコマンドを叩いてやってみました。
便利だったのでメモ。今更感アリアリですが。
ネットワークの作成
今回二つのコンテナを準備してそれぞれを名前解決したいわけですが、そのためにはネットワークを作るといいらしい。
参考:
ふむふむ、やってみましょう。
% docker network create --driver bridge gitlab 【ID文字列】 % docker network inspect gitlab [ { "Name": "gitlab", "Id": "【ID文字列】", "Created": "2017-12-20T05:45:30.1592604Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
Jenkinsの起動
さっき作ったネットワーク gitlab
を使って構築するようにします。コンテナ内の /var/lib/jenkins
はコマンド実行時のディレクトリ直下の jenkins-master
というディレクトリにマウントするようにしました。コマンドは公式ドキュメントを参照。
-name
で名前つけてあげないと、コンテナ間の名前解決できないようなので。
docker run --detach -p 8080:8080 -p 50000:50000 -v `pwd`/jenkins-master:/var/jenkins_home --name jenkins --net=gitlab jenkins/jenkins:lts
GitLabの起動
こっちも公式ドキュメントを参照。
docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 --publish 10080:80 --publish 10022:22 \ --name gitlab \ --restart always \ --volume `pwd`/gitlab/config:/etc/gitlab \ --volume `pwd`/gitlab/logs:/var/log/gitlab \ --volume `pwd`/gitlab/data:/var/opt/gitlab \ --net=gitlab \ gitlab/gitlab-ce:latest
接続確認
手抜きで片方向だけ。
docker exec -it jenkins /bin/bash jenkins@3c121fdb1b14:/$ ping gitlab PING gitlab (172.18.0.3): 56 data bytes 64 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.386 ms 64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.122 ms ^C--- gitlab ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max/stddev = 0.122/0.254/0.386/0.132 ms jenkins@3c121fdb1b14:/$ exit
いけてますね。
GitLabの外部名変更
http://gitlab
になってればいいらしいので。
% docker exec -it gitlab /bin/bash root@gitlab:/# vi /etc/gitlab/gitlab.rb (編集) root@gitlab:/# exit
## GitLab URL ##! URL on which GitLab will be reachable. ##! For more details on configuring external_url see: ##! https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab external_url 'http://gitlab'
こんな感じで書き換えて docker restart
すればうまくいきました。
Jenkins側の名前設定
こちらはGUIでポチポチ変えるだけ。[Jenkinsの管理] > [システムの設定] > [Jenkinsの位置] にて http://jenkins:8080
にします。「リバースプロキシの設定がおかしいようです」と言われますが無視して大丈夫です。
できた!
JenkinsにGitLabプラグイン入れて簡単な設定をしてみたらちゃんと二つのコンテナ間でやりとりできているようです。
ホストマシンをうっかりリブートしてコンテナが止まっていても docker restart
すればさくっと再起動するので楽です。
ただ、手元の環境だとコンテナ2個上げるとホストマシン側が目に見えて遅くなるのが少し困りどころですね。まあ、我慢できなくはないですけど。
執筆報告: 日経Linux 2018年1月号
お声がけいただきまして、日経Linux 2018年1月号 特集1「大幅刷新!始めるなら今 新しいUbuntu完全ガイド」に記事を書きました。12月8日発売。
(上のリンクはアフィなので、踏みたくない人は自分で適当に検索してくださいませ)
この特集自体は読者として読んでもとっても面白いのですが、その中の1ページ……具体的にどこかってのはこのブログをお読みのかたは想像できるのではないかな……プリンターとスキャナについてですえ、を担当させていただきましたです。
筆者の役得としては、才人揃いのUbuntu Japanese Teamの皆さんに混ぜてもらって、その原稿やらゲラやらを先行してみられるというのがあって、いやーみなさん題材の選定から技術的な誠実さから最終的な読み物としての完成度も面白さ本当にすごいなあって思います(小学生みたいな文章ですみません)。その中に交じるのはプレッシャーもありますけどとてもハッピーです。私としては。
相変わらず字数数えられないやつで盛大にはみ出たのですが、そのはみ出た部分についてはまたいずれ。具体的には、ドライバーレス印刷対応の機種だけど俺はベンダー製ドライバーを使いたいんじゃ! という人向けの記述が落ちてます。まあそこ調べが足りなかった(例えばHPLIP対応機種でどうするかとか)ので、別の機会になったほうがよかったですね。
あと、ニュース欄にopenSUSE.Asia Summit 2017 Tokyoのレポートも載ってますが、こちらの執筆もちょっと手伝いました。
ついでにもう一つ、一つ前の号にもLibreOffice 5.4についてのニュースを1ページ書いたのここでお知らせするの忘れてたので。LOOLについてもほんのちょっとですが触れました。
そんなわけで、ぜひ購入お願いします!
(小ネタ)LibreOfficeと笑顔
5日目*1。4日目は Arachansan さん(さんが重複)が書いてくださいましたありがたやありがたや。
今日もまた超小ネタです。
TDFのブログにて「LibreOffice Community Smiles」という連載が始まっております。コミュニティメンバーの素敵な笑顔の写真を毎日紹介するというもの。
- LibreOffice Community Smiles #1 - The Document Foundation Blog
- LibreOffice Community Smiles #2 - The Document Foundation Blog
- LibreOffice Community Smiles #3 - The Document Foundation Blog
みんないい表情!
LibreOfficeコミュニティはそんなに大きくないこともあって家族的だとぼくは思うのですが、それらしい連載だなあって思ってます。過去にTDFブログではAdvent Tipsとかアドベントカレンダー的な連載があったので、これもその一環なのかな。いろんな笑顔がみられると嬉しいですね!
明日はtokyrahさんです。よろしくお願いします!
*1:今日の夜予定があるので、ササッと書いて公開して、Adventarにリンク貼ろうとしたら、 nogajun さんが立候補していただいていました。それならそれでいいやと思ったのですがnogajunさんに気を使っていただいてずらしていただいたので。すみませんすみません。