Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

KotlinTestをAndroidで使ってみる

これは Kotlin Advent Calendar 2017 - Qiita の22日目の記事です。

はじめに

Android Test Night #2 で発表した Try KotlinTest とほぼ同じ内容です。

KotlinTestとは

KotlinTest is a flexible and comprehensive testing tool for Kotlin.

リポジトリ: https://github.com/kotlintest/kotlintest

Kotlin用アサーションライブラリ(テストフレームワーク)一覧

名前 GitHub Star 簡単な説明
Spek 970 Spec風、Java 8 Only (参考: Spek User Guide)
KotlinTest 593 Spec風
expekt 96 メソッドチェーン記法、更新頻度低
AssertK 78 AssertJ風
knit 57 DroidKaigi 2017アプリで利用、更新頻度低

(2017/12/15時点のStar数です)

導入

dependencies {
    testImplementation 'io.kotlintest:kotlintest:2.0.7'
}

基本的な使い方

任意のSpec(下記の例だとStringSpec)を継承して、initブロックにテストケースを書いていきます。

// test cases in init block
class MyTests : StringSpec() {
  init {
    // tests here
  }
}

後述するInterceptingなどを使わない場合(initブロックのみの場合)、下記のようにラムダ記法でも書けます。

// test cases in lambda expression
class MyTests : StringSpec({
  // tests here
})

色々なSpec

Spec 説明
String Spec StringSpec reduces the syntax to the absolute minimum.
Fun Spec FunSpec allows you to create tests similar to the junit style.
Should Spec ShouldSpec is similar to fun spec, but uses the keyword should instead of test.
Word Spec WordSpec uses the keyword should and uses that to nest test blocks after a context string.
Feature Spec FeatureSpec allows you to use feature and scenario.
Behavior Spec BehaviorSpec allows you to use given, when, then.
Free Spec FreeSpec allows you to nest arbitary levels of depth using the keyword - (minus).

String Spec

一番シンプルなSpecです。

class MyTests : StringSpec() {
  init {
    "strings.length should return size of string" {
      "hello".length shouldBe 5
    }
  }
}

Fun Spec

JUnit風に書けるSpecです。

class MyTests : FunSpec() {
  init {
    test("String.length should return the length of the string") {
      "sammy".length shouldBe 5
      "".length shouldBe 0
    }
  }
}

Should Spec

前述のFun Specのtestの代わりにshouldを使ったSpecです。

class MyTests : ShouldSpec() {
  init {
    should("return the length of the string") {
      "sammy".length shouldBe 5
      "".length shouldBe 0
    }
  }
}

Word Spec

コンテキスト毎にshouldキーワードでネストできるSpecです。

class MyTests : WordSpec() {
  init {
    "String.length" should {
      "return the length of the string" {
        "sammy".length shouldBe 5
        "".length shouldBe 0
      }
    }
  }
}

Feature Spec

featurescenarioキーワードを使ったSpecです。

class MyTests : FeatureSpec() {
  init {
    feature("the thingy bob") {
      scenario("should explode when I touch it") {
        // test here
      }
      scenario("and should do this when I wibble it") {
        // test heree
      }
    }
  }
}

Behavior Spec

given, when, thenを使ったSpecです。
Spekのdescribe-on-it記法のような書き方ができます。

class MyTests : BehaviorSpec() {
  init {
    given("a broomstick") {
      `when`("I sit on it") {
        then("I should be able to fly") {
          // test code
        }
      }
      `when`("I throw it away") {
        then("it should come back") {
          // test code
        }
      }
    }
  }
}

Free Spec

-(ハイフン)を使ったSpecです。

class MyTests : FreeSpec() {
  init {
    "String.length" - {
      "should return the length of the string" {
        "sammy".length shouldBe 5
        "".length shouldBe 0
      }
    }
  }
}

Intercepting a Test Case

interceptTestCase()をoverrideすることで、JUnitでいうsetUp()tearDown()をすることができます。
コメントのbeforeのところに前処理、afterのところに後処理を書きます。

class MyTests : StringSpec() {
  override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
    // before

    test() // don't forget to call test()!

    // after
  } 
  init {
    "strings.length should return size of string" {
      "hello".length shouldBe 5
    }
  }
}

Presenterのテスト

とりあえず一番シンプルなStringSpecで使ってみました。
既存コードをAndroid非依存なクラスに抽象化してユニットテストするための第一歩のテストコードを元にしてます。

class StringSpecTest : StringSpec() {
   @Mock lateinit var mRepository: GitHubRepository
   @Mock lateinit var mView: GitHubContract.View

   lateinit var mPresenter: GitHubPresenter
   lateinit var mSchedulerProvider: BaseSchedulerProvider

   override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
       MockitoAnnotations.initMocks(this)
       mSchedulerProvider = ImmediateSchedulerProvider()
       mPresenter = GitHubPresenter(mRepository, mView, mSchedulerProvider)

       test() // don't forget to call test()!
   }

  init {
     "Should init service when presenter started"{
         mPresenter.start()
         verify(mRepository).initService()
     }
     "Should show error when request failed"{
        Mockito.`when`(mRepository.request("")).thenRetrn(Observable.error(Exception())
        mPresenter.request(GitHubPresenterTest.USER)
        verify(mView).showError()
     }
  }
}

気になるところ

  • StringSpec以外だとlateinitな変数がInterceptingできない(?)
  • BehaviorSpecがgiven-when-thenなので、予約語のwhenをエスケープ(when)しないといけない
    • Spekはdescribe-on-it形式なのでエスケープ不要

参考

Try KotlinTest // Speaker Deck
https://speakerdeck.com/rkowase/try-kotlintest

kotlintest/reference.md at master · kotlintest/kotlintest
https://github.com/kotlintest/kotlintest/blob/master/doc/reference.md

Spek User Guide
http://spekframework.org/docs/latest/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
9
Help us understand the problem. What are the problem?