DroidKaigi2019でもSpekに関するセッションがあり、触発されたのでとりあえず使い方をざっくりまとめます。
- DroidKaigi2019でのセッション
以下、Spekのバージョンは2.0.0の前提で記載します。
Spekとは
平たく言うと、Kotlinで書かれたRspec風テストフレームワークです。
スタイルは以下の2種類を提供しています。
SetUp
動作要件
- JDK8
- JUnit5
注意点
- Android integration テストには対応していません
- RobolectricなどはJUnit4で書く必要があるため、両方で動かすには
junit-vintage
なども入れる必要があります
書き方
ここでは specification スタイルでの解説になります。
上記ドキュメントの「Best practices」のセクションに記載されていますが、以下2つの観点でテストを実装すると良いです。
- 副作用を検証する
- 戻り値を検証する
また、specification でのコンセプトは以下の通りです
Suites
-
describe
,context
を用いて仕様のグループ化に利用します。 - 入れ子で定義することもできます。
-
context
はdescribe
の中で定義することができます。 - 通常、以下のように記載することが多いようです。
-
describe
- テスト対象
-
context
- テスト条件
-
Specs
-
it
を用いて何をチェクするかなどのアウトプットに関する内容を記述します。
Skipping
-
describe
,context
,it
にx
をつけることで、そのテストをスキップすることができます。 - もしくは、第二引数にSkip.Yesを指定することでスキップすることもできます。
- テストにスキップの指定をする場合は必ず理由を記述します。
Aliases
-
describe
,context
ブロックのテスト前後に実行したい処理はbefore
/after
やbeforeEach
/afterEach
を利用して記載します。 -
before
/after
はbeforeGroup
/afterGroup
と同じで、各describe
,context
ブロックの開始 / 終了時に呼ばれます -
beforeEach
/afterEach
はbeforeEachTest
/afterEachTest
と同じで宣言したブロック内(入れ子も含む)のテストの前後で呼ばれます - 入れ子のブロックでそれぞれ宣言した場合の呼び出し順序は以下の通りです
- beforeは外側で宣言したものからafterは内側で宣言したものから呼ばれます
- 入れ子になっていても宣言されている
before
がすべて呼ばれた後にbeforeEach
が呼ばれます(after
はその逆)
以下、入れ子での呼び出し順を試したサンプルコードとその実行結果です。
@RunWith(JUnitPlatform::class)
class SpekSampleTest : Spek({
describe("1 - describe") {
before {
println("1 - before")
}
beforeEach {
println("1 - beforeEach")
}
context("1 - 1 - context") {
before {
println("1 - 1 - before")
}
beforeEach {
println("1 - 1 - beforeEach")
}
it("1 - 1 - it") {
println("1 - 1 - it")
}
afterEach {
println("1 - 1 - afterEach")
}
after {
println("1 - 1 - after")
}
}
context("1 - 2 - context") {
it("1 - 2 - it") {
println("1 - 2 - it")
}
}
afterEach {
println("1 - afterEach")
}
after {
println("1 - after")
}
}
describe("2 - describe") {
before {
println("2 - before")
}
beforeEach {
println("2 - beforeEach")
}
context("2 - context") {
it("2 - 1 - it") {
println("2 - 1 - it")
}
it("2 - 2 - it") {
println("2 - 2 - it")
}
}
afterEach {
println("2 - afterEach")
}
after {
println("2 - after")
}
}
})
1 - before
1 - 1 - before
1 - beforeEach
1 - 1 - beforeEach
1 - 1 - it
1 - 1 - afterEach
1 - afterEach
1 - 1 - after
1 - beforeEach
1 - 2 - it
1 - afterEach
1 - after
2 - before
2 - beforeEach
2 - 1 - it
2 - afterEach
2 - beforeEach
2 - 2 - it
2 - afterEach
2 - after
記述例
@RunWith(JUnitPlatform::class)
class SpekSampleTest : Spek({
val set = mutableListOf<String>()
describe("adding an item") {
beforeEachTest {
set.add("item")
}
it("should contain item") {
assertEquals("item", set[0])
}
it("should have a size > 0") {
assertTrue(set.size > 0)
}
}
})
最後に
あとは実際に書く時に、describe
やcontext
などにどんな記述で書くか、テストをスキップさせる場合はどうするかなどをチーム内でルール化しておくと良いと思います。