Kotestのテストスタイル(Testing Styles)
テストスタイル
Kotestはさまざまなテストスタイル(Testing Styles)を提供している。ここでは、Kotestがサポートする10種類のテストスタイルを紹介する。
| Testing Style | 影響を受けたもの |
|---|---|
| Fun Spec | ScalaTest |
| String Spec | A Kotest original |
| Should Spec | A Kotest original |
| Describe Spec | Javascript frameworks and RSpec |
| Behavior Spec | BDD frameworks |
| Word Spec | ScalaTest |
| Free Spec | ScalaTest |
| Feature Spec | Cucumber |
| Expect Spec | A Kotest original |
| Annotation Spec | JUnit |
それぞれのスタイルは特定の構文に従い、状況に応じて選択して使用できる。各テストスタイルにはそれぞれの特徴と長所、短所があるため、プロジェクトの要件に合わせて適切なスタイルを選択できる。
以下では各テストスタイルについて説明し、基本的な使い方を、いずれかのスタイルを継承するサンプルコードで確認する。
FunSpec
FunSpecは関数ベースのテストスタイルである。各テストケースを関数として作成し、テスト本体をその関数内に記述する。
FunSpecを使用すると、テストを説明する文字列引数を指定してtest関数を呼び出し、テスト自体をラムダとして渡すことでテストを作成できる。どのスタイルを使うべきか迷う場合は、このスタイルを使うのがよい。
以下はFunSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
class FunSpecTest : FunSpec({
test("FunSpecの例 - 加算テスト") {
val result = 2 + 2
result shouldBe 4
}
})
通常の方法に加えて、xcontextおよびxtestのバリエーションを使用してテストを無効にできる。
class MyTests : FunSpec({
context("this outer block is enabled") {
xtest("this test is disabled") {
// test here
}
}
xcontext("this block is disabled") {
test("disabled by inheritance from the parent") {
// test here
}
}
})
StringSpec
StringSpecは文字列ベースのテストスタイルである。自然言語に近い構文でテストを記述できる。主に各テストケースを文字列として記述し、テスト本体を波括弧{}内に記述する。
StringSpecは構文を最小限に抑える。テストコードでは、文字列の後にラムダ式を書くだけでよい。
以下はStringSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class StringSpecTest : StringSpec({
"StringSpecの例 - 加算テスト" {
val result = 2 + 2
result shouldBe 4
}
})
テストに設定を追加することもできる。
class MyTests : StringSpec({
"strings.length should return size of string".config(enabled = false, invocations = 3) {
"hello".length shouldBe 5
}
})
ShouldSpec
ShouldSpecはFunSpecに似ているが、testの代わりにshouldキーワードを使って記述する。主にテストケースの動作を仕様として表現するために使用される。
以下はShouldSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.matchers.shouldBe
class ShouldSpecTest : ShouldSpec({
should("ShouldSpecの例 - 加算テスト") {
val result = 2 + 2
result shouldBe 4
}
})
テストは1つ以上のコンテキストブロックにネストすることもできる。
class MyTests : ShouldSpec({
context("String.length") {
should("return the length of the string") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
}
})
通常の方法に加えて、xcontextおよびxshouldのバリエーションを使用してテストを無効にできる。
class MyTests : ShouldSpec({
context("this outer block is enabled") {
xshould("this test is disabled") {
// test here
}
}
xcontext("this block is disabled") {
should("disabled by inheritance from the parent") {
// test here
}
}
})
DescribeSpec
DescribeSpecテストスタイルはdescribe / itキーワードを使用するため、RubyやJavaScriptの背景を持つ人にとってなじみやすいスタイルである。describe構文を使用してテストケースをグループ化し、テストは1つ以上の説明ブロックにネストする必要がある。主にテストケースを説明するために使用される。
以下はDescribeSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
class DescribeSpecTest : DescribeSpec({
describe("DescribeSpecの例") {
it("加算テスト") {
val result = 2 + 2
result shouldBe 4
}
}
})
通常の方法に加えて、xdescribeおよびxitのバリエーションを使用してテストを無効にできる。
class MyTests : DescribeSpec({
describe("this outer block is enabled") {
xit("this test is disabled") {
// test here
}
}
xdescribe("this block is disabled") {
it("disabled by inheritance from the parent") {
// test here
}
}
})
BehaviorSpec
振る舞い駆動開発(BDD, Behavior Driven Development)スタイルの書き方である。
given、when、thenブロックを使用してテストを記述する。
主要な機能や動作に焦点を当ててテストケースを作成する。
以下はBehaviorSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
class BehaviorSpecTest : BehaviorSpec({
Given("BehaviorSpecの例") {
When("加算テスト") {
val result = 2 + 2
Then("2と2を足すと4になるべきである") {
result shouldBe 4
}
}
}
})
Given、WhenにAndキーワードを使用して深さを追加することもできる。
class MyTests : BehaviorSpec({
given("a broomstick") {
and("a witch") {
`when`("The witch sits on it") {
and("she laughs hysterically") {
then("She should be able to fly") {
// test code
}
}
}
}
}
})
通常の方法に加えて、xgiven、xwhen、xthenのバリエーションを使用してテストを無効にできる。
class MyTests : BehaviorSpec({
xgiven("this is disabled") {
When("disabled by inheritance from the parent") {
then("disabled by inheritance from its grandparent") {
// disabled test
}
}
}
given("this is active") {
When("this is active too") {
xthen("this is disabled") {
// disabled test
}
}
}
})
WordSpec
WordSpecは文字列ベースのテストスタイルである。自然言語に近い構文を使用してテストを記述する。
shouldキーワードを使用して、コンテキスト文字列の後にテストをネストする。各テストケースを文字列として記述し、テスト本体をshouldブロック内に記述する。
以下はWordSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
class WordSpecTest : WordSpec({
"WordSpecの例 - 加算テスト" should {
"2と2を足すと4になるべきである" {
val result = 2 + 2
result shouldBe 4
}
}
})
また、別のネストレベルを追加できるようにwhenキーワードもサポートしている。whenはKotlinのキーワードであるため、バッククォートまたは大文字のバリエーションを使用する必要がある点に注意する。
class MyTests : WordSpec({
"Hello" When {
"asked for length" should {
"return 5" {
"Hello".length shouldBe 5
}
}
"appended to Bob" should {
"return Hello Bob" {
"Hello " + "Bob" shouldBe "Hello Bob"
}
}
}
})
FreeSpec
FreeSpecは自由形式のテストスタイルである。テストケース間の依存性を減らし、独立してテストを記述できる。
FreeSpecを使用すると、外側のテストでは-(マイナス)キーワードを使用し、最終テストではテスト名だけを使用して、任意の深さでネストできる。
以下はFreeSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
class FreeSpecTest : FreeSpec({
"FreeSpecの例 - 加算テスト" - {
val result = 2 + 2
"2と2を足すと4になるべきである" {
result shouldBe 4
}
}
})
FeatureSpec
FeatureSpecは機能(Feature)ベースのテストスタイルである。各テストケースを機能またはシナリオに合わせて作成する。
FeatureSpecを使用すると、機能とシナリオを使用できる。これはCucumberを使ったことがある人にはなじみがあるだろう。Cucumberと完全に同じではないが、キーワードはそのスタイルを模倣している。
以下はFeatureSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FeatureSpec
import io.kotest.matchers.shouldBe
class FeatureSpecTest : FeatureSpec({
feature("FeatureSpecの例") {
scenario("加算テスト") {
val result = 2 + 2
result shouldBe 4
}
}
})
通常の方法に加えて、xfeatureおよびxscenarioのバリエーションを使用してテストを無効にできる。
class MyTests : FeatureSpec({
feature("this outer block is enabled") {
xscenario("this test is disabled") {
// test here
}
}
xfeature("this block is disabled") {
scenario("disabled by inheritance from the parent") {
// test here
}
}
})
ExpectSpec
テスト仕様をexpect構文を使用して記述する。主に期待結果を明示するために使用される。
ExpectSpecはFunSpecやShouldSpecに似ているが、expectキーワードを使用する。
以下はExpectSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.ExpectSpec
import io.kotest.matchers.shouldBe
class ExpectSpecTest : ExpectSpec({
context("ExpectSpecの例") {
expect("加算テスト") {
val result = 2 + 2
2 + 2 shouldBe 4
}
}
})
テストは1つ以上のコンテキストブロックにネストすることもできる。
class MyTests : ExpectSpec({
context("a calculator") {
expect("simple addition") {
// test here
}
expect("integer overflow") {
// test here
}
}
})
通常の方法に加えて、xcontextおよびxexpectのバリエーションを使用してテストを無効にできる。
class MyTests : ExpectSpec({
context("this outer block is enabled") {
xexpect("this test is disabled") {
// test here
}
}
xcontext("this block is disabled") {
expect("disabled by inheritance from the parent") {
// test here
}
}
})
AnnotationSpec
アノテーションベースのテストスタイルである。特定のアノテーションを使用してテストを記述する。
JUnitから移行する場合、AnnotationSpecはJUnit 4/5のようなアノテーションを使用するSpecである。Specクラスに定義した関数に@Testアノテーションを追加するだけでよい。
JUnitと同様に、before tests/specsとafter tests/specsにアノテーションを追加できる。
@BeforeAll / @BeforeClass
@BeforeEach / @Before
@AfterAll / @AfterClass
@AfterEach / @After
テストを無視するには@Ignoreを使用する。
以下はAnnotationSpecのサンプルコードである。
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.AnnotationSpec
import io.kotest.matchers.shouldBe
class AnnotationSpecExample : AnnotationSpec() {
@BeforeEach
fun beforeTest() {
println("Before each test")
}
@Test
fun test1() {
1 shouldBe 1
}
@Test
fun test2() {
3 shouldBe 3
}
}
テストの構造化
Kotestでは、テストを構造化するためにcontextブロックを使用し、テスト階層またはグループとしてテストケースを作成できる。これにより、テストを論理的に分類し、関連するテストケースをまとめて管理できる。
例えば、次はテストグループを使用してテストを構造化する例である。
import io.kotest.core.spec.style.FunSpec
class ContextTest : FunSpec({
context("Calculator tests") {
test("Addition") {
// Test logic for addition
}
test("Subtraction") {
// Test logic for subtraction
}
}
context("String tests") {
test("Length") {
// Test logic for string length
}
test("Concatenation") {
// Test logic for string concatenation
}
}
})
上の例では、contextブロックを使用してテストを分類して分けている。
contextブロックはFunSpec、ShouldSpec、ExpectSpecでのみ使用できる。