Kotest Testing Styles

Kotest provides a variety of testing styles. This page explains the supported testing styles and how to use them.

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.


References