Writing reusable tests with Kotest's Test Factory
Tags: kotest, kotlin, testing
Published: 2020-04-16 20:02:59

TL;DR - How to use [Kotest][1] 4.0.0 new Test Factory feature to include reusable blocks of test in your test suite

# Hello, Kotliners!

In this article we'll see how to use a feature from the [Kotest Framework][1] to create reusable tests in your test suite, for cases in which you test multiple classes exactly the same way.

## Declaring a Test Factory function

It's very easy to create a reusable test. You must first pick your favourite style among [Kotest's test styles][2]. You can pick any of them, but my personal favourite (and the one I'll use here) is `FunSpec`.

Declaring a test using `FunSpec` is as simple as:

```kotlin
class MySpec : FunSpec({
    test("Foo should not be equal to bar") {
        "foo" shouldNotBe "bar"
    }
})
```

Declaring the same test, but this time as a test factory is very similar:

```kotlin
// Top level val
val myReusableTest = funSpec {
    test("Foo should not be equal to bar") {
        "foo" shouldNotBe "bar"
    }
}
```

However, there are some cases in which having the same test will produce a different result if called multiple times. For cases which our tests depend on some parameters, we might rather want to use use this as a function:

```kotlin
fun myReusableTest(myParameter: String) = funSpec {
    test("$myParameter should not be equal to bar") {
        myParameter shouldNotBe "bar"
    }
}
```

Both cases can be included in any `Spec` by using the `include` function:

```kotlin
class MyTest : FunSpec({
    test("My usual test") {
        // ...
    }
    
    include(myReusableTest("A"))
    include(myReusableTest("B"))
    
    test("Another usual test") {
        // ...
    }
})
```

And as expected, the tests from the test factory are applied!
![](https://i.imgur.com/kWN7UH8.png)

## A small example

Now that we understand the basic concept, let's play a little bit with a more realistic example: Calculators! ~~really realistic, everyone implements calculators~~

Let's suppose we have 3 types of calculators:
1. A Sum Calculator
2. A Subtraction Calculator
3. A Complete Calculator, that can do both operations.

```kotlin
interface SumOp {
    fun sum(a: Int, b: Int): Int
}

object SumCalculator : SumOp {
    override fun sum(a: Int, b: Int) = a + b
}

interface SubtractionOp {
    fun subtract(a: Int, b: Int): Int
}

object SubtractionCalculator : SubtractionOp {
    override fun subtract(a: Int, b: Int) = a - b
}
```

To test these implementations, we would create a `Spec` for each:
```kotlin
class SumCalculatorTest : FunSpec({
    test("Sum 2 and 2 should be 4") {
        SumCalculator.sum(2, 2) shouldBe 4
    }
})

class SubtractionCalculatorTest : FunSpec({
    test("4 minus 2 should be 2") {
        SubtractionCalculatorTest.subtract(4, 2) shouldBe 2
    }
})
```

When implementing our `CompleteCalculator`, we should test these behaviours as well.

```kotlin
object CompleteCalculator : SumOp, SubtractionOp {
    
    override fun sum(a: Int, b: Int) = a + b

    override fun subtract(a: Int, b: Int) = a - b
}

class CompleteCalculatorTest : FunSpec({
    test("Sum 2 and 2 should be 4") {
        CompleteCalculator.sum(2, 2) shouldBe 4
    }

    test("4 minus 2 should be 2") {
        CompleteCalculator.subtract(4, 2) shouldBe 2
    }
})
```
We have ended up writing the same test that we already wrote for sum calc and sub calc. This is duplication that we'd like to try and avoid.

Now let's exercise what we learned previously. Writing tests through composition:

```kotlin
fun sumTests(calc: SumOp) = funSpec {
    test("Sum 2 and 2 should be 4") {
        calc.sum(2, 2) shouldBe 4
    }
}

fun subtractionTests(calc: SubtractionOp) = funSpec {
    test("4 minus 2 should be 2") {
        calc.subtract(4, 2) shouldBe 2
    }
}

class SumCalculatorTest : FunSpec({
    include(sumTests(SumCalculator))
})

class SubtractionCalculatorTest : FunSpec({
    include(subtractionTests(SubtractionCalculator))
})

class CompleteCalculatorTest : FunSpec({
    include(sumTests(CompleteCalculator))
    include(subtractionTests(CompleteCalculator))
})

```

And _voilà!_ We successfully reused a test for more than one scenario!

## What could this be useful for?

In the real world we won't be implementing calculators as often as we want to. So, **why would I use this kind of factory and composition**?

We created these **Test Factories** for any kind of repeatable test pattern. For example:

- If you need to test that every `Repository<MyEntity>` creates a database entity with a new ID
- If your `cache` and your actual database return data in the same format
- If sending a `POST` request to some endpoint always returns the payload in a specific format
- If all instances of a `sealed class` have their class' name on the `toString` function

And much more scenarios that you might be exploring with!



[1]: https://github.com/kotest/kotest
[2]: https://github.com/kotest/kotest/blob/master/doc/styles.md