sbt-androidでRobolectricのテストを実行

RobolectricはActivityなどのAndroidのライブラリに依存するテストを実機やエミュレータを用いずに、通常のJVM上で実行可能にするフレームワークです。
エミュレータを使わないので高速に動作し、JenkinsなどのCIサーバ上でも通常のテストと同様そのまま実行することができます。
動作原理は以下の記事が詳しいです。

Robolectric - Androidのクラスを端末やエミュレータなしで単体テ…:ITpro

今回はこのRobolectricのテストをsbt-androidから実行する方法を説明します。
方法は大きく2つあり、1つはrobospecsを使ってSpecs2で実行する方法、もう1つはRobolectricが標準で対応しているJUnitを使う方法です。それぞれ以下のメリット、デメリットがあります。

robospecsを使う方法

  • メリット
    • Specs2を使用してテストを書くことができる
  • デメリット
    • Robolectricのバージョンが古い(1.1)

JUnitを使う方法

  • メリット
    • 最新のRobolectricを使用できる
  • デメリット
    • JUnit 4を使用しなければならない

僕は最新のRobolectricを使用したかったので、RobolectricのテストはJUnitを使うことにしました。Androidフレームワークに依存しないPOSOなScalaのクラスはSpecs2でテストを書いています。

導入方法

  1. build.sbtのlibraryDependenciesに以下を追加

      "org.robolectric" % "robolectric" % "2.1.1" % "test",
    "junit" % "junit" % "4.11" % "test",
    "com.novocode" % "junit-interface" % "0.8" % "test->default"

    junit-interfaceはsbtでJUnitのテストを実行するためのものです。

  2. Robolectricのテストを追加

    src/test/scala/com/example/test/MainActivityTest.scala

    package com.example.test

    import org.junit.runner._
    import org.robolectric.annotation.Config
    import org.robolectric.{Robolectric, RobolectricTestRunner}
    import org.junit.{Test, Before}
    import com.example.{TR, MainActivity}
    import org.junit.Assert._

    @RunWith(classOf[RobolectricTestRunner])
    @Config(manifest="src/main/AndroidManifest.xml")
    class MainActivityTest {
    var activity: MainActivity = null

    @Before
    def setUp() {
    activity = Robolectric.buildActivity(classOf[MainActivity]).create().get()
    }

    @Test
    def searchText() {
    val searchText = activity.findView(TR.search_text)
    assertNotNull(searchText)
    }
    }

    上のテストはあくまでサンプルなので、自分のプロジェクトに合わせて適宜書き換えて下さい。ポイントは以下の部分です。

    @Config(manifest="src/main/AndroidManifest.xml")

    sbt-androidのプロジェクトではAndroidManifest.xmlはsrc/mainにあると思いますが、JavaAndroidプロジェクトではプロジェクトのルートディレクトリに通常存在します。Robolectricは実行時にカレントディレクトリ(通常はプロジェクトのルートディレクトリ)にあるAndroidManifest.xmlを読もうとするため、上記のようにしてAndroidManifest.xmlを指定しないとエラーとなってしまいます。またnew Activity()のように直接Activityをnewすると警告が出たため、Robolectric.buildActivityを使用してActivityを取得しています。

  3. 実行

    $ sbt test

    Specs2、JUnitのテストが同じプロジェクトに混在していてもきちんと全てのテストが実行され、結果も集計されて表示されます。ほんとよくできてますよね!