カスタムViewが想定通りに描画されているかテストする

カスタムViewを作って、しかもそれがCanvasを使って描画するようなものだった場合、どうやって動作確認をしていますか? 私はこれまで実機で動かして、目視で確認していました。Viewの見た目なので目視で確認するしかないんですけどね。それを手動でやっていました。 しかしつい先日、手動での確認が難しい案件に出くわしました。それは端末のセンサーの値を読み取って、その値にあわせてカスタムViewの描画が変わるようなものでした。これは手動で確認したくとも難しいです。 例えば心拍数を元に描画が変わるカスタムViewを想像してみてください。心拍数が120を超えたら特殊な表示を行う仕様だと思ってください。実機でそれを確認しようと思ったら、心拍数を上げるべく毎回運動しなきゃいけない、なんてことになるわけです。 そういったViewの描画、見た目の確認がしたい。こういうの、みんなどうやってテストしているのだろう。それが今回の出発点です。 サンプルプロジェクトをGitHubに置いてみたので良かったら見てみてください。というよりコードの解説はこの記事では一切ありませんので、GitHubでみてください。 やり方書かないのもあれなので、追記しました。 サンプルについて TextViewの周りを線でデコレーションするカスタムViewがテスト対象です。どこを描画するかを指定してinvalidate()すると、TextViewの周りに線が描画されます。onDrawメソッドをオーバーライドして、Canvasを使って線を描いています。 今回はこの描画がちゃんとできるかを確認する、というそんなテストです。 スクリーンショットを撮って確認しよう Viewの描画を確認したいわけですから、ユニットテストでは確認できません。 そこでまず思いついたのが、スクリーンショットを撮って、その画像で確認できたらいいんじゃないかというものでした。以前にEspresso+Spoonで自動的にスクリーンショットを撮るテストの話を見たのを覚えていたので、これを使えばいけそうと考えました。 問題が2つ しかしSpoonを使ってスクショを撮るには、WRITE_EXTERNAL_STORAGEパーミッションが必要になります。プロダクト側で必要なら問題ありませんが、そうでない場合はテストのためだけに不要なパーミッションを追加することになります。できればそれは避けたい。 また、スクショはActivityを起動してそれを撮影することになるわけですが、実際に対象のViewを表示するActivityがテストに適した作りになっているとは限りません。 例えばこのサンプルプロジェクトでも、MainActivityを使ってテストできなくもありません。Espressoを使ってボタンを押すようにすれば、カスタムViewの描画は切り替わります。しかしこのMainActivityの仕様だと、カスタムViewの上と下に線を描画した状態をテストできません。 つまり、実際に使うActivityとは別にテストのためだけのActivityが欲しいわけです。 ではそんなActivityをプロダクションに混ぜるのかという話になりますが、それも避けたい。 テスト用のProduct Flavorsを用意する そこでテスト用のプロダクトフレーバーを作成することでこれを回避しました。これもあまりスマートなやり方ではなく、できれば避けたかったのですが仕方ありません。 debugビルドにだけテスト用のパーミッション、Activityを含めるという方法もなくはないのですが、プロダクトフレーバーで切り分けてしまったほうが潔いかなと思ったのです。 テスト用のAndroidManifestとActivityさえ用意できれば、後は簡単です。 余談、androidTestに専用Activityを作ればいいんじゃないかという考え ちなみに私は最初、androidTest配下にテスト用のActivityを追加して、それ経由でテストすればいいんじゃないかと考えました。しかしそれはうまくいきません。 なぜなら、androidTestに配置したコードはテスト用のAPKにコンパイルされるからです。 私は今までずっと勘違いしていました。androidTestに書いたテストを実行したら、mainに配置してるテスト対象コードにテストコードを追加したAPKが作成されて、それでテストが実行されてるんだと思ってました。どうもそうではなくて、普通のAPKを単にテスト用APKで外部から操作してただけなんですね。 https://stackoverflow.com/questions/27826935/android-test-only-permissions-with-gradle 作り方 まずproductFlavorを追加します。サンプルでは普段使うやつをDefault、Viewのテスト用のものをUiTestとしました。ここではUiTestを追加するとして書いていますので、適宜読み替えてください。 まずapp/build.gradleにproductFlavorの設定を追加します。applicationIdSuffixはお好みで。 android { productFlavors { Default { } UiTest { applicationIdSuffix ".uiTest" } } // そのままだとUiTestReleaseもbuildVariantに追加されてしまうので、それに対処 android.variantFilter { variant -> if(variant.buildType.name.equals('release') && variant.getFlavors().get(0).name.equals('UiTest')) { variant.setIgnore(true); } } } EspressoとSpoonのセットアップ Espresso Spoon プロジェクトルートのbuild.gradleに追記。 classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2' app/build.gradleに追記。 apply plugin: 'spoon' android { defaultConfig { // 追加しないと多分テストがうまく走ってくれないと思います。 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } } dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) androidTestCompile('com.android.support.test:runner:0.5', { exclude group: 'com.android.support', module: 'support-annotations' }) androidTestCompile 'com.squareup.spoon:spoon-client:1.6.4' } プロダクトフレーバー用のディレクトリを作成 プロジェクトツールウィンドウのスコープをProjectに変更して、手動でディレクトリを作成します。(何か他にいい方法知ってれば教えてください)
Read full post gblog_arrow_right

リアルタイムで心拍数を計測できるHeart Rate Monitor Wearをリリースしました

リアルタイムで心拍数を計測・表示するAndroid Wearアプリをリリースしました。 特徴はリアルタイムで心拍数が分かること、Wear端末で計測した心拍数がそのままスマホに表示可能な機能があることです。 アプリはGoogle Playで公開中です。 開発の動機 運動不足解消とダイエットのために、室内でフィットネスバイクを漕いでいたのですが、あまり効果が現れませんでした。そんな中、ダイエット目的の運動は心拍数を元に運動すると効率がいいという情報を目にしました。 心拍数がだいたい40%〜60%くらいになるように運動すると、脂肪燃焼の効率がいいらしいです。 そこでAndroid Wearを使って心拍数を確認しながら運動することを考え、開発を開始しました。 しかし運動中にいちいち腕時計の画面を確認しないといけないのはちょっと面倒です。特に私はスマホでAbemaTVを見ながら運動していました。心拍数を確認するのに手元に視線を動かすことは、運動から気がそれるだけでなく、番組からも目を離すことになってしまいます。 どうせスマホの画面を見ているのだから、画面の右隅にでも計測中の心拍が表示されればいいのに。そんな思いからこのアプリが誕生しました。 スマホへのオーバーレイ表示 こんな感じで画面の右上に半透明で心拍数が表示されます。 YouTubeなどで動画を見ながらでも心拍数を確認できます。今はやりのポケモンGOをやりながらでも確認できます。(ただポケモンGOだと心拍数をコントロールするような運動はしないでしょうけど) 心拍の推移を記録 このアプリは心拍数をリアルタイムで表示するだけでなく、その推移を記録します。これはWear端末のみで運動した場合でも記録できます。 例えばWear端末のみを装着してジョギングを行う→家に帰ってスマホでジョギング中の心拍数の変化を振り返る、といった使い方ができます。 使い方 Android Wearのアプリ一覧からこのアプリを起動すれば心拍数の計測が始まります。 スマホ側にも起動ボタンを用意しているので、そちらからも起動できます。 起動・終了ともに、スマホとWearが通信可能な状態なら、スマホ側で起動すればWear側も起動します。終了に関しても同様です。 計測中はWear端末の画面上に心拍数が表示されます。アンビエントモードに対応しているので、バッテリーに配慮した作りになっています。 スマホに心拍数を表示するよう権限を許可していれば、心拍計測が始まれば自動的にスマホ側にも心拍数が表示されるようになっています 計測を終了するには、Wear端末上のアプリ画面で右に向かってスワイプします。スマホで計測終了ボタンを押してもOKです。 計測したデータはログとしてスマホで後から確認することができます。ただし、あまりにも長い時間計測をした場合、データがうまく保存できない可能性があります。2〜3時間は大丈夫だと思いますが、端末の性能やセンサーの精度などにも影響されるので一概には言えません。 必要な権限について ボディセンサー 他のアプリに重ねて表示 端末スリープの無効化 ネットワークアクセス関連 最初2つに関しては、お使いの端末がAndroid 6.0以上の場合には、実行時に許可するかどうかを選択できます。 ボディセンサー Android Wear端末で心拍数を読み取るために必須です。この権限を許可しない場合このアプリは動作しません。 他のアプリに重ねて表示 スマホ側で心拍数を確認するオーバーレイ表示を行うために必要です。この権限は許可しなくても心拍数の計測はできます。 権限を許可しない場合はオーバーレイ表示ができないので、スマホで心拍数を確認できなくなります。 端末スリープの無効化 Android Wearのアンビエントモードに対応しているため、端末スリープを無効化する表示が出ています。実際にはちゃんとスリープします。 ネットワークアクセス関連 広告の表示や、アプリの機能改善のためのアクセス解析、クラッシュログ送信サービス利用のために必要となっています。 個人情報の取得・送信は行っていません。 どのようなデータが送信され、どう取り扱われるかについてはプライバシーポリシーをご覧ください。 精度について 計測精度は目安程度に思ってもらったほうが良いかもしれません。 腕時計のセンサーで計測するわけなので、センサーが皮膚としっかり密着していないと正しい数値が出ないようです。 具体的に言うと、比較的腕を動かさないですむサイクリングマシーン(フィットネスバイク)による運動だと安定した計測ができているように感じます。汗を拭くのに腕を動かしたりすると数値が極端に下がったりします。 バーベルを使った筋トレ中に計測をしてみましたが、こちらはまったく安定しません。握ったり動かしたりとセンサーと皮膚の間に空間がしょっちゅうできる上に、よほどきつくバンドを締めていないかぎり腕時計がズレます。そのため、心臓がバクバクしてるのが体感で分かるレベルにもかかわらず、心拍数が安静時と同じ数値を示したりしていました。 とはいえ、スマホで動画を見ながらのながら運動に最適だと思います。ぜひ使ってみて感想をお聞かせください。 アプリはGoogle Playで公開中です。

Kotlinを使っていて感じるつらさ

ちなみにこの記事を書いたのはかなり昔のことなので、今はだいぶ環境が変わっている。マシンスペックによる辛さも含まれていたので、現在ではだいぶ解消されていると思って読んで欲しい。

なにせ3年もまえの記事だからね・・・あんまり真に受けないように。19年時点の私はだいぶKotlinラバーですから。

Read full post gblog_arrow_right