Kotest Testing Styles
Testing Styles
Kotest provides several testing styles. This page introduces the 10 testing styles supported by Kotest.
| Testing Style | Inspired by |
|---|---|
| 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 |
Each style follows its own syntax, and you can choose the one that best fits the situation. Each testing style has its own characteristics, strengths, and trade-offs, so select the style that matches your project requirements.
The following sections explain each testing style and show basic usage through examples that extend one of these style classes.
FunSpec
FunSpec is a function-based testing style. Each test case is written as a function, and the test body is written inside that function.
With FunSpec, you create a test by calling the test function with a descriptive string argument and passing the test itself as a lambda. If you are not sure which style to choose, this is a good default.
The following example shows FunSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
class FunSpecTest : FunSpec({
test("FunSpec example - addition test") {
val result = 2 + 2
result shouldBe 4
}
})
In addition to the normal style, you can disable tests by using the xcontext and xtest variants.
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 is a string-based testing style. It lets you write tests with syntax close to natural language. Each test case is usually written as a string, and the test body is written inside braces {}.
StringSpec keeps the syntax minimal. You only need to write a lambda expression after the string in the test code.
The following example shows StringSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class StringSpecTest : StringSpec({
"StringSpec example - addition test" {
val result = 2 + 2
result shouldBe 4
}
})
You can also add configuration to a test.
class MyTests : StringSpec({
"strings.length should return size of string".config(enabled = false, invocations = 3) {
"hello".length shouldBe 5
}
})
ShouldSpec
ShouldSpec is similar to FunSpec, but it uses the should keyword instead of test. It is commonly used to describe the behavior of a test case.
The following example shows ShouldSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.matchers.shouldBe
class ShouldSpecTest : ShouldSpec({
should("ShouldSpec example - addition test") {
val result = 2 + 2
result shouldBe 4
}
})
Tests can also be nested inside one or more context blocks:
class MyTests : ShouldSpec({
context("String.length") {
should("return the length of the string") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
}
})
In addition to the normal style, you can disable tests by using the xcontext and xshould variants.
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
The DescribeSpec testing style uses the describe / it keywords, so it feels familiar to people with a Ruby or JavaScript background. It uses describe syntax to group test cases, and tests must be nested inside one or more description blocks. This style is mainly used to describe test cases.
The following example shows DescribeSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
class DescribeSpecTest : DescribeSpec({
describe("DescribeSpec example") {
it("addition test") {
val result = 2 + 2
result shouldBe 4
}
}
})
In addition to the normal style, you can disable tests by using the xdescribe and xit variants:
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
This is a behavior-driven development (BDD) testing style.
Tests are written with given, when, and then blocks.
Test cases are written with a focus on major features or behavior.
The following example shows BehaviorSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
class BehaviorSpecTest : BehaviorSpec({
Given("BehaviorSpec example") {
When("addition test") {
val result = 2 + 2
Then("2 plus 2 should be 4") {
result shouldBe 4
}
}
}
})
You can also add more depth by using the And keyword with Given and When:
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
}
}
}
}
}
})
In addition to the normal style, you can disable tests by using the xgiven, xwhen, and xthen variants:
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 is a string-based testing style. It uses syntax similar to natural language.
It uses the should keyword to nest tests after a context string. Each test case is written as a string, and the test body is written inside a should block.
The following example shows WordSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
class WordSpecTest : WordSpec({
"WordSpec example - addition test" should {
"2 plus 2 should be 4" {
val result = 2 + 2
result shouldBe 4
}
}
})
It also supports the when keyword when you want to add another level of nesting. Note that when is a Kotlin keyword, so you must use backticks or the uppercase variant.
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 is a free-form testing style. It reduces dependencies between test cases and lets you write tests independently.
With FreeSpec, you can nest tests to any depth by using the - keyword for outer tests and using only the test name for the final test:
The following example shows FreeSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
class FreeSpecTest : FreeSpec({
"FreeSpec example - addition test" - {
val result = 2 + 2
"2 plus 2 should be 4" {
result shouldBe 4
}
}
})
FeatureSpec
FeatureSpec is a feature-based testing style. Each test case is written around a feature or scenario.
FeatureSpec lets you use features and scenarios, which will feel familiar to anyone who has used Cucumber. It is not exactly the same as Cucumber, but the keywords imitate that style.
The following example shows FeatureSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.FeatureSpec
import io.kotest.matchers.shouldBe
class FeatureSpecTest : FeatureSpec({
feature("FeatureSpec example") {
scenario("addition test") {
val result = 2 + 2
result shouldBe 4
}
}
})
In addition to the normal style, you can disable tests by using the xfeature and xscenario variants:
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
This style writes test specifications with the expect syntax. It is mainly used to express expected results.
ExpectSpec is similar to FunSpec and ShouldSpec, but it uses the expect keyword.
The following example shows ExpectSpec:
package com.devkuma.kotest.tutorial.testingstyles
import io.kotest.core.spec.style.ExpectSpec
import io.kotest.matchers.shouldBe
class ExpectSpecTest : ExpectSpec({
context("ExpectSpec example") {
expect("addition test") {
val result = 2 + 2
2 + 2 shouldBe 4
}
}
})
Tests can also be nested inside one or more context blocks:
class MyTests : ExpectSpec({
context("a calculator") {
expect("simple addition") {
// test here
}
expect("integer overflow") {
// test here
}
}
})
In addition to the normal style, you can disable tests by using the xcontext and xexpect variants.
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
This is an annotation-based testing style. Tests are written by using specific annotations.
If you are migrating from JUnit, AnnotationSpec is a spec that uses annotations like JUnit 4/5. You only need to add the @Test annotation to functions defined in the spec class.
As with JUnit, you can add annotations for before tests/specs and after tests/specs.
@BeforeAll / @BeforeClass
@BeforeEach / @Before
@AfterAll / @AfterClass
@AfterEach / @After
Use @Ignore to ignore a test.
The following example shows 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
}
}
Structuring Tests
Kotest supports structuring tests with context blocks, which let you define test cases as a hierarchy or group. This helps you classify tests logically and manage related test cases together.
For example, the following code structures tests with test groups:
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
}
}
})
In this example, tests are classified and separated with context blocks.
The context block can be used only in FunSpec, ShouldSpec, and ExpectSpec.