Testing System Properties and System Environment with Kotest
Tags: kotlin, kotest, testing
Published: 2019-07-21 21:16:50

TL;DR: Test code that uses `System.getenv()` or `System.getProperty()` with easy-to-use functions with [Kotest](https://github.com/Kotest/Kotest)



# Hello Kotliners!

In this article we'll discuss a little bit two features of Kotest:

- System Environment extensions
- System Property extensions

They're very similar on their implementation and use, and are very easy to use!


## The problem

In the JVM world, some (many) times our code depends on what the system provides us, usually through *Environment Variables* or *Properties* passed to the JVM.

However, neither is very easy to test when we want to use it.

- The `System.getenv()` map [is immutable](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/solaris/classes/java/lang/ProcessEnvironment.java#l78). We can't set it up using plain Java
- Messing with `System.setProperty()` manually forces us to do all the setup and tear-down, and becomes very verbose and cumbersome




|[![](https://i.imgur.com/taONeg2.jpg)](https://unsplash.com/@vheath)|
|:--:|
|Photo by [Victoria Heath](https://unsplash.com/@vheath?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on Unsplash|


## The solution

Kotest provides some extensions and listeners to handle these scenarios. The complete documentation can be found [here](https://github.com/Kotest/Kotest/blob/master/doc/extensions.md#system-extensions)


### System Environment Extensions

If the piece of code under test is small enough and used only once, we can use the `withSystemEnvironment`. It has overloads, but we'll use the `key` and `value` one. There's an overload expecting a `Pair<K,V>` and one expecting a `Map<K,V>`

``` kotlin
class EnvironmentTest : FunSpec() {
    init {
        test("Changing environment") {
            withSystemEnvironment("fooKey", "barValue") {
                System.getenv("fooKey") shouldBe "barValue"
            }
        }
    }
}
```

### System Properties Extensions

In the same fashion as above, we have the function `withSystemProperties`, that works very similarly:

``` kotlin
class PropertyTest : FunSpec() {
    init {
        test("Changing System Properties") {
            withSystemProperties(mapOf("fooKey" to "barValue", "booKey" to "batValue")) {
                System.getProperty("fooKey") shouldBe "barValue"
                System.getProperty("booKey") shouldBe "batValue"
            }
        }
    }
}
```

### Cleanup

After the execution, the previous values will be set to what they were before. What is already in the map will remain untouched during the execution.


### Test Listeners

Perhaps your code require the specific properties in every test. You can configure the `SystemPropertyTestListener` and the `SystemEnvironmentTestListener` for that:

``` kotlin
class SystemEnvironmentTestListenerTest : FunSpec() {

    override fun listeners() = listOf(
            SystemEnvironmentTestListener("fooKeyEnv", "barValueEnv"),
            SystemPropertyTestListener("fooKeyProp", "barValueProp")
        )

    init {
        test("System Property") {
            System.getProperty("fooKeyProp") shouldBe "barValueProp"
        }
        test("System Environment") {
            System.getenv("fooKeyEnv") shouldBe "barValueEnv"
        }
    }
}
```

The original values will reset after the test execution.

### Project Listener

You can also set these globally by using the `SystemEnvironmentProjectListener` and the `SystemPropertyProjectListener`. More information on Project Configuration can be found in [Kotest's Docs](https://github.com/Kotest/Kotest/blob/master/doc/reference.md#project-config)

```kotlin
class ProjectConfig : AbstractProjectConfig() {
    override fun listeners(): List<TestListener> = listOf(SystemEnvironmentProjectListener("foo", "bar"))
}
```


### Override Mode

All the above extensions accept a parameter of the enum `OverrideMode`.

- SetOrOverride - If the key we want to set is already in the properties/environment, we want to override it for the test
- SetOrIgnore - If the key is already present, we'll not override it and the test will continue
- SetOrError (default) - If the key is already present, interrupt the test with an Exception

These are very useful if you need to use, for example, a property in the global listener, but want to override it in a specific test.

```kotlin
withSystemProperty("foo", "bar", OverrideMode.SetOrOverride) {
    // foo is overrided here
}
```

### Extensions in action

I've published 2 small and simple libs that exercise these listeners and extensions:

- [SimpleProperties](https://github.com/Kerooker/SimpleProperties) - That reads values both from Environment and Properties. [Test Class](https://github.com/Kerooker/SimpleProperties/blob/master/src/test/kotlin/com/kerooker/simpleproperties/internal/profile/SystemProfileLoaderTest.kt). It's even possible to see both extensions [used at once](https://github.com/Kerooker/SimpleProperties/blob/a5650444029050a80f5fdc6128a45f73f8a299b6/src/test/kotlin/com/kerooker/simpleproperties/internal/profile/SystemProfileLoaderTest.kt#L53)
- [SimpleFeatureThrottler](https://github.com/Kerooker/SimpleFeatureThrottler) - A similar approach can be seen in [this test class](https://github.com/Kerooker/SimpleFeatureThrottler/blob/master/src/test/kotlin/com/kerooker/simplefeaturethrottler/internal/SystemPercentageFetcherTest.kt)