テストを実行した跡にレポートを自動的に開く

gradle経由でテストタスクを実行すると、テスト結果のレポートがbuild/reportsディレクトリに出力される。このレポートファイルを確認するのに、毎回ProjectウィンドウをProject表示に切り替えて、ディレクトリを掘ってファイルをブラウザで開くようにするのは手間である。そこでテスト実行後に自動的に開くようにした。 もっとスマートにやる方法があるのではないかと思うのだが、Gradleがよくわからなくて私には無理だった。以下のタスクをapp/build.gradleに追加した。 task openReportJvmTest(type: Exec) { workingDir "build/reports/tests/testProdDebugUnitTest" commandLine 'open' args "index.html" } task openReportInstrumentedTest(type: Exec) { workingDir "build/reports/androidTests/connected/flavors/MOCK" commandLine 'open' args "index.html" } openReportJvmTest.onlyIf { !ciBuild && !travisBuild } openReportInstrumentedTest.onlyIf { !ciBuild && !travisBuild } tasks.withType(AbstractTestTask) { task -> if (task.name == "testProdDebugUnitTest") { task.finalizedBy openReportJvmTest } } tasks.withType(com.android.build.gradle.internal.tasks.AndroidTestTask) { task -> if (task.name == "connectedMockDebugAndroidTest") { task.finalizedBy openReportInstrumentedTest } } openReportXXXというのがテストレポートをブラウザで開くタスク。開くディレクトリを直書きしているが、がんばればタスクによって開くディレクトリを変えることはできるだろう。openコマンドを使っているが、Windowsだときっと動かない。 既存のテストタスクのFinalizerタスクとして実行するよう指定しているが、CI上ではopenコマンドが認識できないので失敗してしまう。そこでCI上では実行しないようにonlyIfで指定している。 pluginで定義されているテストタスクの後に実行させたいのだが、tasks.withTypeを使うことで各タスクの参照を得ている。直接タスクを指定すると「そんなプロパティはない」と言われたり、nullだったりしてうまくいかなくてこういう形に行き着いた。 取得したテストタスクそれぞれでfinalizedByを使ってレポートを開くタスクを指定している。dependsOnを使うとテストが全てパスしていればレポートが開くが、1つでもテストが失敗しているとopenReportタスクが実行されないので、finalizedByを使っている。 http://gradle.monochromeroad.com/docs/userguide/more_about_tasks.html 余談 そもそもRun configurationでテストを実行できるようにすればいいのではないかと思うかもしれない。というか私も最初はその方法をとった。 しかしAndroid Instrumented Testに関してはできたが、Android JUnitではできなかった。 個別のテストをクラスを指定して実行する分には問題ないが(テストコードを表示して実行するやり方)、プロジェクトに存在するテストコードをまとめて実行する方法が取れなかった。Javaで書かれたテストコードは実行されても、Kotlinで書かれたテストが漏れてしまうのである(このプロジェクトはJavaで書かれたテストコードとKotlinで書かれたテストコードが混在している)。 しょうがないのでCIでも実行するgradleのテストタスクを走らせる方法をとることにしたのだ。

com.google.android.gms.tasks.Task<T>のユニットテスト

com.google.android.gms.tasks.Task<T>にまつわり処理でユニットテストを書きたいということがある。例えばFirebaseを使うときにそんな思いに駆られることがあるだろう。 私はFirebase Realtime Databaseの処理をラップするクラスを作成したときに、ユニットテストを書きたいと思った。 何らかの処理を要求した後、返ってくるのがこのTask<T>というクラスである。リクエストはすべて非同期で処理されるので、処理の結果はaddOnSuccessListenerなどを使ってリスナーをセットして、そのリスナーの中でリクエストの結果を受け取るようなコードになる。 ユニットテスト書くときに困るポイントは2つ。 どうやって`Task`を生成するか 処理結果をリスナーで受け取るにはどうするか Taskの生成 Task<T>を生成するにはTasks.forResult()などのメソッドを利用するとよい。 com.google.android.gms.tasks.Tasksがファクトリメソッドみたいなものなので、こいつ経由で目当てのTask<T>が作れる。こうすることで起点部分の問題はクリアできる。 https://developers.google.com/android/reference/com/google/android/gms/tasks/Tasks リスナーの注意点 処理結果をリスナーで受け取る際は注意が必要である。 利用する際にはaddOnSuccessListener()に渡す引数は1つ、この場合は成功時に行う処理だけ指定していると思う。 この場合、OnSuccessListenerのonSuccessメソッドがメインスレッドで呼び出されることに注意が必要である。 実はTask<T>に対して設定するOnSuccessListener、OnFailureListener、OnCompliteListenerはそれぞれ、特に指定を行わない限り必ずAndroidのメインスレッド上で呼び出される。このメインスレッドで呼び出されるというのは、Looper.getMainLopper()で取得されるメインスレッドである。 つまり、JVMユニットテスト(testディレクトリに配置するユニットテスト)ではリスナーの処理が呼び出されないことに注意が必要である。 この問題に対応するためには、Instrumentation testとして実行する(androidTestディレクトリに配置する)か、addOnSuccessListenerでリスナーを設定する際に、スレッドを指定(Executorを指定)してやればよい。 val executor: Executor = Executors.newSingleThreadExecutor() Tasks.forResult("test") .addOnSuccessListener(executor, OnSuccessListener { println(it) }) ちなみにそのままでは処理タイミングの問題で、リスナーの処理が呼び出される前にテストメソッドが終了してしまう場合がある。そのため、CountDownLatchなどを利用するなどして、リスナーの処理が呼び出されるのを待ち合わせたりする必要がある。 待ち合わせが必要になるのは、Instrumentation Testでも同じである。たとえTasks.forResult()を使って即座に処理が完了するTask<T>にリスナーを設定しようとも、タイミングによって呼ばれないことが起こりうるので、リスナーの呼び出しのテストを行う場合には必ず待ち合わせの処理を挟むことが必要。

こう書いたらこう動くよねを確認するためにユニットテストを活用する

私はわりとカジュアルにユニットテストを使っている(と思う)。クラスやメソッドの振る舞いをテストするさらに前段階の状態で使っている。 「こういうコードを書いたらこうなると思ってるんだけど、合ってるよね?」というのを確認するためのテストコードをユニットテストに書いてしまうのである。ノイズにしかならないので、普通はこんな使い方しないと思うが、Androidの文脈においていちいち端末で実行して動作を確認するよりは早いと思っているので、個人的には便利に使っている。 実例 上記のリポジトリでは、ThreeTenAbpの使い方を確認するテスト用のコードなんかが散らばっているが、こんな感じでライブラリの動作を確認したり、最近だとRxJavaでOperatorを連結した処理の確認をしたりするのにユニットテストを利用している。 testディレクトリに適当なクラスを作って、そこでJUnitスタイルで動きを確認したいコードを書くだけである。 私はKotlinでコードを書くことが多くなってきているので、Kotlinの拡張関数の動きを確認するためにユニットテストを活用していたりする。例えば空文字をsplit()したときにサイズがどうなるのか、なんてのを確認したりするわけだ。 class StringSplitTest { @Test fun split_empty() { val test = "".split("\t") test.size.should.equal(1) } } このコードではexpektというライブラリを使ってアサーションを行っている。 実装を追えよという話ではあるのだけど、ふとした疑問をさっと確認するのには便利だと思うのだけどどうだろうか。わざわざ実機なりエミュレータ上で実行して、デバッガで確認して・・・なんてするよりは絶対に早い。 たぶんチームで開発しているとかいう状況だと、こんなテストコードが残されると邪魔でしかないと思うが、個人的に確認する分には弊害はないだろう。消すのを忘れてコミットしちゃうおそれがあるとか、そんなレベルの話。 私の場合はぼっちで開発しているので、こういうレベルでもテストコードを書いていれば、ユニットテストのやり方の勉強にもなっていいかなと思っている。

KotlinでUnitテストでassertionに何を使うか

KotlinでUnitテストを書く際に、assertionに何を使うかという話。 私の場合特にこだわりがあるわけではないのだが、基本的には多数派に従いたいという気持ちが強い。しかしながら、Kotlinのテストのassertionに使うならこれ一択、みたいなものがない(と思っている)ので、いつも困る。 Javaなら特に何もせずともhamcrestのassertThat(sut.getHoge()).is("fuga")みたいなassertionを使っていればいいのだが、Kotlinの場合はisが予約語であるという問題がある。いちいちisをバッククォートでくくらなければならず美しくない。 knit https://github.com/ntaro/knit 日本ではいちばん有名なやつだと思われる(DroidKaigiのアプリでも使われていたはずなので)。 sut.getHoge().should be "fuga"みたいな感じで使う。 ただrepositoryを追加しないと組み込めないので私はあまり使っていない。build.gradleに一行追加するだけの話なんだけどね・・・。 expekt https://github.com/winterbe/expekt knitに似たsut.getHoge().should.equals("fuga")みたいな感じのAPI。リポジトリの追加が必要ないので、私はこっちをよく使っている。 ただメンテされてないのが玉に瑕。 kotlintest https://github.com/kotlintest/kotlintest assertionライブラリではなくて、Spec風のテストを書くためのライブラリ。 ただsut.getHoge() shouldBe "fuga"みたいなassertionが含まれている。 SpekというSpec風のテストを書くためのライブラリがあるが、こちらはJava8でなければ使えないという制約があって、Androidのプロジェクトに適用するのはなんだか難しそうで手を出していない。 kotlintestはネストしたテストを書きやすくて、これいいかもとか思ったこともあったのだけれど、androidTestに組み込むとメソッドカウントがオーバーしてしまうので使えない。 assertk https://github.com/willowtreeapps/assertk Android Weeklyで流れてきて知った。AssertJチックなassertionライブラリで、Kotlinで使うならコレのが便利なのだろうか。 assert(sut.getHoge()).isEqualTo("fuga")みたいな感じで使う。 しかし個々最近はshould系ばかり使ってきているので、最初にassertを書かないといけない形式はめんどくさいと感じてしまっている。 AssertJ https://joel-costigliola.github.io/assertj/ Javaのコードも考慮に入れるならJavaで使えるライブラリを使うのがいいのだろう。 私はJavaだとhamcrest使っとけばいいやな人だったので、Javaでassersionライブラリを何使うかなんて特に気にしたことはなかった。 みんなが使っているものが知りたい みんなはどれを使っているのだろうか。他にこれが使いやすいみたいなのがあったら教えて欲しい。 私はexpektを使う傾向が強いが、揺らいでいる。 正直なところassertionさえできればなんでもいいのだろうからして、自分が使いやすいものを使えばいいってだけの話なのだろうけれども。 https://discuss.kotlinlang.org/t/what-assertions-library-do-you-use/1809 ここをみると、kotlintestが多いっぽい。

Realmのテストのやり方を知りたい

Realmを使ってみました。ちなみに私は、今まではGreenDAOとAndroid Ormaしか使ったことがありません。 とりあえずCRUD操作のやり方をつかもうとテストを書いてみました。テストの書き方が根本的に間違っている可能性が無きにしもあらずですが、こんな感じで作りました。 public class FilterDataSourceRealmTest { private static RealmConfiguration config; private static FilterDataSourceRealm sut; @BeforeClass public static void initializeTest() { config = new RealmConfiguration.Builder() .name("test_realm") .deleteRealmIfMigrationNeeded() .build(); sut = new FilterDataSourceRealm(config); } @Before public void setUp() { Realm.deleteRealm(config); } @After public void tearDown() { Realm.deleteRealm(config); } @Test public void insertFilter() throws Exception { final CountDownLatch latch = new CountDownLatch(1); sut.insertFilter("test.com/"); sut.getFilter("test.com/") .subscribe(new Action1<UriFilter>() { @Override public void call(UriFilter uriFilter) { assertThat(uriFilter.getFilter(), is("test.com/")); latch.countDown(); } }); latch.await(2, TimeUnit.SECONDS); } } テスト対象のコード(一部抜粋)はこんな感じです。 public class FilterDataSourceRealm implements FilterDataSource { private RealmConfiguration config; public FilterDataSourceRealm(RealmConfiguration config) { this.config = config; } @Override public void insertFilter(String insert) { Realm realm = Realm.
Read full post gblog_arrow_right

Instrumentation Testで生成されるAPKは何をしているのだろう

以前、Viewの描画をテストするためのリポジトリを作りました。記事はこれです。 Viewが想定通り描画されているか確認するため、Spoonを使ってスクリーンショットを撮るようにしました。GitHubにあげたコードでは、TextViewの周りに枠線を描画するCustomViewを作成し、その枠線が描画されるかを確認するというものでした。 しかしSpoonで撮ったスクリーンショットでは、右側と下側に描画されるはずの線が表示されていません。実機で動かすと描画されているのですが、スクリーンショット上では見えない。 Spoonのバグなんじゃないかななんて最初は思っていたのですが、調べてみると原因は違うところにありました。いえ、Spoonのせいではないということはわかったのですが、じゃあなぜそうなるのかというところが分からないので困っている状態です。 Spoonのスクリーンショットで線が描画されない理由は、右と下の線が画面外に描画されてしまっているからです。 CustomViewは右側・下側に描画する位置を、onDrawメソッドの引数で渡ってくるCanvasのサイズ(canvas.getWidth()とcanvas.getHeight())を使って描画しています。 実機で実行した場合、ここに渡ってくるCanvasのサイズは、CustomViewと同じサイズになっているようなので、TextViewの周りに枠線が描画されます。 一方で、androidTestで実行した場合、このcanvas.getWidth()で得られる数値は、想定したものよりはるかに大きい数値になります。数値の大きさから察するに、画面全体と同じ大きさになっているような気がします。 実機で実行した場合: 10-05 17:35:09.972 1992-1992/jp.gcreate.sample.viewdrawingtest.uiTest D/test: canvas:android.view.GLES20RecordingCanvas@30073153, height:96, width:983 10-05 17:35:15.412 1992-1992/jp.gcreate.sample.viewdrawingtest.uiTest D/test: canvas:android.view.GLES20RecordingCanvas@2a498faf, height:96, width:983 androidTestで実行した場合: 10-05 17:37:37.955 3888-3888/jp.gcreate.sample.viewdrawingtest.uiTest D/test: canvas:android.view.GLES20RecordingCanvas@375e49fb, height:1436, width:983 10-05 17:37:37.982 3888-3888/jp.gcreate.sample.viewdrawingtest.uiTest D/test: canvas:android.graphics.Canvas@1cad5ead, height:1919, width:1079 androidTestで実行すると、渡ってくるCanvasが実機の場合と異なるようです。 onDrawメソッドで渡されるCanvasとは一体何なのかという点についても、私はよく分かっていないのですが、androidTestで実行されるtest用のAPK(この場合app-UiTest-debug-androidTest.apk)が何をやっているのかもよく分からなくなってきました。 androidTestを実行すると、実機上に画面が表示され、テストコードに書いた動きが実行されていくので、それは全てtest用のAPKで実行されているのだとばかり思っていました。しかしそう考えると、実機で表示されている画面では枠線が描画されているのに、Spoonで撮影したスクリーンショットには映っていないことの理由が説明できません。 そんなことを考えていると、Instrumentation Testとは一体何なのかがよく分からなくなってきました。

カスタム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

Android Testing Codelabやってみた

Android Testing CodelabはEspressoなどのテストツールの使い方を学べるサンプルです。 全篇Englishですが、大体雰囲気でわかるレベルだと思います。 このレッスンをやれば、アプリ開発におけるテストツールの使い方、 JUnitを使ったユニットテスト(Andoridの端末を必要としないテスト) Espressoを使ったUIテスト(実機orエミュレータを必要とするテスト) が学べます。 ちょっとお得だなと思ったのが、アプリがMVPパターンで作られていることです。テストツールの使い方の勉強のついでに、MVPパターンも学べるなんて一石二鳥だな、なんて思ったのでちょっとやってみました。 思っていたよりもざっくりとした解説なので、雰囲気をつかめるものくらいに考えるといいと思います。 それでもテストのやり方よく分かっていない私からすると、学びの多いレッスンでした。 パッケージを機能で分けるやり方もある このサンプルではパッケージをレイヤーごとではなく機能ごとに分けてありました。(機能ごとというよりはActivityごとに近い分け方だと思いましたが) 私はこれまでずっと、ModelはModelパッケージに、というレイヤーごとにパッケージを分けていましたが、「機能ごとに分けた方が見やすくていいだろ」と書かれていて目からうろこでした。 ProductFlavorを使ったクラスの切り替え テストのためにモック用のクラスに差し替えるやり方の解説があります。 XMLのリソースファイル(strings.xmlなど)は異なるFlavorで同一のリソース名が存在した場合、Flavorのものが優先されmainで定義したリソースは上書きされます。 一方でJavaのクラスだと挙動が異なり、同一名のクラスが存在するとエラーになります。そのため、全てのFlavor共通で利用するクラスだけをmainに配置し、切り替えが必要なクラスはFlavorのディレクトリに配置するという工夫が必要になります。 モックを利用したJUnitのテスト Mockitoを使ったJUnit4のテストのやり方が勉強になりました。これは単純に私がMockitoの使い方がよく分かっていなかったからですが。 @Mockアノテーションに寄る初期化とか、ArgumentCaptorの使い方とか。 EspressoによるUIテスト Espresso-IntentsによるIntentのモック方法、Espresso-Contribを使ったナビゲーションドロワーのUIテストなどが紹介されています。 Espressoテストの章になると、一部修正が必要な部分がありましたが、それもまた勉強になりました。(EditTextへ文字入力をエミュレートした後は、croseKeyboard()しないとエラーになるとか、上へボタンを参照する部分が英語以外の環境だとエラーになるとか)

Android StudioでJunit4によるテストを走らせる

基本的にはここに書いてあるとおりにやればいいだけの話です。 準備 /app/build.gradleのdependenciesにjunitを追加します。 testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.9.5' この際に注意が必要なのは、androidTestCompileとtestCompileは別物であるということです。 何が別物かというと、テストコードを配置するディレクトリがそれぞれ違います。 その名の通りandroidTestCompileはandroidTestディレクトリに配置したテストコードのコンパイル時にだけ使うライブラリです。同じくtestCompileはtestディレクトリに配置したテストコードのみに使われるライブラリになります。 なお、androidTestディレクトリは自動的に作成されていますが、testディレクトリは自分で作らなければなりません。(ディレクトリは/app/src/test/java/パッケージ名にしてやればOK) androidTestとtestの切り替え Build Variantsウィンドウを開くと、Test Artifactという欄があります。 Android Instrumentation Testsを選択していると、androidTestディレクトリ以下にあるテストコードが有効化されます。有効化されるというのが適切なのかは分かりませんが、Android Studioからコンパイル対象のソースコードであると認識されます。 Unit Testsに切り替えると、testディレクトリが有効化されます。試しに切り替えてみると、androidTestディレクトリの色が変わって、テストコードのアイコンに赤いJアイコンが出るようになると思います。 IDE上からテストを実行しようと思うと、このBuild Variantsをいちいち切り替えないといけないのが面倒くさいかもしれません。 しかし、ターミナルからGradleを使って実行する場合は、このTest Artifactsの切り替えはしなくてもいいみたいです。Gradleからテストを実行する場合、./gradlew connectedAndroidTestがAndroid Instrumentation Testsを、./gradlew testがUnit Testsを選択してテストを実行するのと同じになります。この場合のテスト結果は/app/build/reports/testsの中に出力されます。 テストコードはViewやActivityなどのUIに関するテストをandroidTestディレクトリに、純粋なJavaコードのテストはtestディレクトリに置くように工夫すべきでしょう。そうしてやれば、ユニットテストにかかる時間を削減できて幸せになれると思います。

Espressoを使ってUIテストを書いてみた

Android Studio 1.2でEspresso2.1を使ったUIテストをやってみました。 テストの実行に実機(エミュレータ)が必要なのが面倒くさいですが、実機無しでテストが実行できるようにする方が面倒くさい(というかやり方がわからない)ので、テストができるだけマシだと考えることにしました。 テストを実行する際に、開発者オプションでアニメーションの無効化をしておかないと、アニメーションのせいで同じテストが失敗することがあるのは注意が必要かもしれません。 しかしながら、Android Support Libraryに入っているおかげでテストを実行するまでのハードルが低いのはうれしいところです。 また、EspressoによるUIテストの書き方も非常にシンプルで分かりやすいと思います。 準備 詳しい手順はここに書いてあるとおりです。Get started – android-test-kit まずは/app/build.gradleにテストで利用するライブラリを追加します。 dependencies {
// Testing-only dependencies
androidTestCompile 'com.android.support.test:runner:0.2'
androidTestCompile 'com.android.support.test:rules:0.2'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'
} /app/build.gradleにtestInstrumentationRunnerを追記します。場所はdefaultConfigの中です。 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 更に以下のおまじないも追加します。 packagingOptions { exclude 'LICENSE.txt' } 最終的な/app/build.gradle 適宜読み替えて使ってください。 apply plugin: 'com.android.application'
android {
compileSdkVersion 22 buildToolsVersion "22.0.1" defaultConfig { applicationId "jp.gcreate.product.developersettingsshortcut"
minSdkVersion 15 targetSdkVersion 22
versionCode 1
versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } packagingOptions {
exclude 'LICENSE.txt' } } dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.jakewharton:butterknife:6.1.0'
// Testing-only dependencies androidTestCompile 'com.android.support.test:runner:0.2'
androidTestCompile 'com.android.support.test:rules:0.2'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1' } 一時的なバグ? Warning:Conflict with dependency 'com.
Read full post gblog_arrow_right