これは 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
feature
とscenario
キーワードを使った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/