Posted at
KotlinDay 22

KotlinTestをAndroidで使ってみる

More than 1 year has passed since last update.

これは 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/